- Migrate config for API 15
- Migrate database for API 15
- Allow usage of new target source
- Implement first tell target option
This commit is contained in:
Infi
2026-04-30 02:59:58 +02:00
parent 68810e23c1
commit b4cb8b25ec
56 changed files with 1286 additions and 616 deletions
+2 -10
View File
@@ -1,15 +1,7 @@
<Project Sdk="Dalamud.NET.Sdk/14.0.2">
<Project Sdk="Dalamud.NET.Sdk/15.0.0">
<PropertyGroup>
<Version>1.34.7</Version>
<Version>1.35.0</Version>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<LangVersion>latest</LangVersion>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
</PropertyGroup>
<ItemGroup>
+5 -10
View File
@@ -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)
{
+35 -74
View File
@@ -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;
}
}
+36 -11
View File
@@ -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,
/// <summary>The player currently controlled by the local client.</summary>
LocalPlayer = 1 << XivChatRelationKind.LocalPlayer,
/// <summary>A player in the same 4-man or 8-man party as the local player.</summary>
PartyMember = 1 << XivChatRelationKind.PartyMember,
/// <summary>A player in the same alliance raid.</summary>
AllianceMember = 1 << XivChatRelationKind.AllianceMember,
/// <summary>A player not in the local player's party or alliance.</summary>
OtherPlayer = 1 << XivChatRelationKind.OtherPlayer,
/// <summary>An enemy entity that is currently in combat with the player or party.</summary>
EngagedEnemy = 1 << XivChatRelationKind.EngagedEnemy,
/// <summary>An enemy entity that is not yet in combat or claimed.</summary>
UnengagedEnemy = 1 << XivChatRelationKind.UnengagedEnemy,
/// <summary>An NPC that is friendly or neutral to the player (e.g., EventNPCs).</summary>
FriendlyNpc = 1 << XivChatRelationKind.FriendlyNpc,
/// <summary>A pet (Summoner/Scholar) or companion (Chocobo) belonging to the local player.</summary>
PetOrCompanion = 1 << XivChatRelationKind.PetOrCompanion,
/// <summary>A pet or companion belonging to a member of the local player's party.</summary>
PetOrCompanionParty = 1 << XivChatRelationKind.PetOrCompanionParty,
/// <summary>A pet or companion belonging to a member of the alliance.</summary>
PetOrCompanionAlliance = 1 << XivChatRelationKind.PetOrCompanionAlliance,
/// <summary>A pet or companion belonging to a player not in the party or alliance.</summary>
PetOrCompanionOther = 1 << XivChatRelationKind.PetOrCompanionOther,
}
+10 -10
View File
@@ -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),
};
}
+59
View File
@@ -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,
};
}
+21 -21
View File
@@ -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<TextCommand>? 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<uint>(),
_ => []
};
if (ids.Length == 0)
+66 -47
View File
@@ -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<ChatType, ChatSource>? InactivityHideChannels;
[Obsolete("Use InactivityHideChannelsV2 instead")]
public Dictionary<ChatType, ChatSource> InactivityHideChannels = [];
public Dictionary<ChatType, (ChatSource, ChatSource)> InactivityHideChannelsV2 = [];
public bool InactivityHideExtraChatAll = true;
public HashSet<Guid> 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<string> 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<ChatType, ChatSource> ChatCodes = new();
public Dictionary<ChatType, (ChatSource, ChatSource)> SelectedChannels = new();
public bool ExtraChatAll;
public HashSet<Guid> 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.
/// </summary>
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<Message> messages) : IReadOnlyList<Message>, IDisposable
public class RLockedMessageList(SemaphoreSlim lockSlim, List<Message> messages) : IReadOnlyList<Message>, IDisposable
{
public IEnumerator<Message> GetEnumerator()
{
@@ -446,31 +465,31 @@ internal class Tab
}
}
internal class UsedChannel
public class UsedChannel
{
internal InputChannel Channel = InputChannel.Invalid;
internal List<Chunk> Name = [];
internal TellTarget? TellTarget;
public InputChannel Channel = InputChannel.Invalid;
public List<Chunk> 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(),
+12 -8
View File
@@ -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
{
+13 -8
View File
@@ -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<float> 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("ĂăÂâÎîȘșȚț");
+14 -14
View File
@@ -20,7 +20,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using InteropGenerator.Runtime;
using Lumina.Text.ReadOnly;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
namespace ChatTwo.GameFunctions;
@@ -35,21 +35,21 @@ internal sealed unsafe class Chat : IDisposable
// Client::UI::AddonChatLog.OnRefresh
[Signature("40 53 57 41 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 4D 8B F8", DetourName = nameof(ChatLogRefreshDetour))]
private Hook<ChatLogRefreshDelegate>? ChatLogRefreshHook { get; init; }
private Hook<ChatLogRefreshDelegate>? ChatLogRefreshHook = null!;
private delegate byte ChatLogRefreshDelegate(nint log, ushort eventId, AtkValue* value);
// Replace with CS version later
[Signature("48 89 5C 24 ?? 55 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 83 B9", DetourName = nameof(ContextMenuTellInForayDetour))]
private Hook<ContextMenuTellInForayDelegate>? ContextMenuTellInForayHook { get; set; }
private Hook<ContextMenuTellInForayDelegate>? ContextMenuTellInForayHook = null!;
private delegate void ContextMenuTellInForayDelegate(RaptureShellModule* module, Utf8String* playerName, Utf8String* worldName, ushort worldId, ulong accountId, ulong contentId, ushort reason);
private Hook<AgentChatLog.Delegates.ChangeChannelName> ChangeChannelNameHook { get; init; }
private Hook<RaptureShellModule.Delegates.ReplyInSelectedChatMode>? ReplyInSelectedChatModeHook { get; init; }
private Hook<RaptureShellModule.Delegates.SetContextTellTarget>? SetChatLogTellTargetHook { get; init; }
private readonly Hook<AgentChatLog.Delegates.ChangeChannelName>? ChangeChannelNameHook;
private readonly Hook<RaptureShellModule.Delegates.ReplyInSelectedChatMode>? ReplyInSelectedChatModeHook;
private readonly Hook<RaptureShellModule.Delegates.SetContextTellTarget>? SetChatLogTellTargetHook;
// Pointers
[Signature("48 8D 35 ?? ?? ?? ?? 8B 05", ScanType = ScanType.StaticAddress)]
private readonly char* CurrentCharacter = null!;
[Signature("48 8D 1D ?? ?? ?? ?? 8B 05", ScanType = ScanType.StaticAddress)]
private readonly char* LastTypedCharacter = null!;
private Plugin Plugin { get; }
@@ -64,7 +64,7 @@ internal sealed unsafe class Chat : IDisposable
private long LastPlayerNameDisplayTypeRefresh;
private PlayerNameDisplayType CurrentPlayerNameDisplayType = PlayerNameDisplayType.FullName;
internal Chat(Plugin plugin)
public Chat(Plugin plugin)
{
Plugin = plugin;
Plugin.GameInteropProvider.InitializeFromAttributes(this);
@@ -131,7 +131,7 @@ internal sealed unsafe class Chat : IDisposable
// If this function ever returns 0, it returns null instead.
internal uint? GetChannelColor(ChatType type)
{
var parent = new ChatCode((ushort) type).Parent();
var parent = type.Parent();
switch (parent)
{
case ChatType.Debug:
@@ -169,7 +169,7 @@ internal sealed unsafe class Chat : IDisposable
if (eventId != 0x31 || value == null || value->UInt is not (0x05 or 0x0C))
return ChatLogRefreshHook!.Original(log, eventId, value);
if (Plugin.Functions.KeybindManager.DirectChat && CurrentCharacter != null)
if (Plugin.Functions.KeybindManager.DirectChat && LastTypedCharacter != null)
{
// FIXME: this whole system sucks
// FIXME v2: I hate everything about this, but it works
@@ -177,7 +177,7 @@ internal sealed unsafe class Chat : IDisposable
{
string? input = null;
var utf8Bytes = MemoryHelper.ReadRaw((nint)CurrentCharacter+0x4, 2);
var utf8Bytes = MemoryHelper.ReadRaw((nint)LastTypedCharacter+0x4, 2);
var chars = Encoding.UTF8.GetString(utf8Bytes).ToCharArray();
if (chars.Length == 0)
return;
@@ -230,7 +230,7 @@ internal sealed unsafe class Chat : IDisposable
private CStringPointer ChangeChannelNameDetour(AgentChatLog* agent)
{
var ret = ChangeChannelNameHook.Original(agent);
var ret = ChangeChannelNameHook!.Original(agent);
if (agent == null)
return ret;
@@ -572,6 +572,6 @@ internal sealed unsafe class Chat : IDisposable
// second before the cutscene actually starts, because the game sets
// the cutscene conditions before processing the skip.
var raptureAtkUnitManager = RaptureAtkUnitManager.Instance();
return raptureAtkUnitManager == null || raptureAtkUnitManager->UiFlags.HasFlag(UIModule.UiFlags.Chat);
return raptureAtkUnitManager == null || raptureAtkUnitManager->UiFlags.HasFlag(UiFlags.Chat);
}
}
+4 -3
View File
@@ -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);
}
+3 -3
View File
@@ -14,7 +14,7 @@ using FFXIVClientStructs.FFXIV.Client.UI.Info;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Excel;
using Lumina.Excel.Sheets;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
namespace ChatTwo.GameFunctions;
@@ -22,7 +22,7 @@ internal unsafe class GameFunctions : IDisposable
{
#region Hooks
[Signature("E8 ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B D0 49 8D 4F", DetourName = nameof(ResolveTextCommandPlaceholderDetour))]
private Hook<ResolveTextCommandPlaceholderDelegate>? ResolveTextCommandPlaceholderHook { get; init; }
private Hook<ResolveTextCommandPlaceholderDelegate>? ResolveTextCommandPlaceholderHook = null!;
private delegate nint ResolveTextCommandPlaceholderDelegate(nint a1, byte* placeholderText, byte a3, byte a4);
#endregion
@@ -132,7 +132,7 @@ internal unsafe class GameFunctions : IDisposable
agent->AddonId = addon->Id;
// Skips early return
atkStage->TooltipManager.Flag1 |= 2;
atkStage->TooltipManager.TooltipType |= 2;
addon->Show(false, 15);
}
+1 -1
View File
@@ -1,7 +1,7 @@
namespace ChatTwo.GameFunctions.Types;
[Flags]
internal enum ModifierFlag
public enum ModifierFlag
{
None = 0,
Shift = 1 << 0,
+1 -1
View File
@@ -1,6 +1,6 @@
namespace ChatTwo.GameFunctions.Types;
internal enum TellReason
public enum TellReason
{
Direct = 0,
PartyFinder = 1,
+27 -9
View File
@@ -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);
}
@@ -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 = [];
}
/// <summary>
+1 -1
View File
@@ -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)
+4 -6
View File
@@ -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<OverrideInfo, object> OverrideChannelGate { get; }
private ICallGateSubscriber<Dictionary<string, uint>, Dictionary<string, uint>> ChannelCommandColoursGate { get; }
@@ -26,10 +26,8 @@ internal sealed class ExtraChat : IDisposable
private Dictionary<Guid, string> ChannelNamesInternal { get; set; } = new();
internal IReadOnlyDictionary<Guid, string> ChannelNames => ChannelNamesInternal;
internal ExtraChat(Plugin plugin)
internal ExtraChat()
{
Plugin = plugin;
OverrideChannelGate = Plugin.Interface.GetIpcSubscriber<OverrideInfo, object>("ExtraChat.OverrideChannelColour");
ChannelCommandColoursGate = Plugin.Interface.GetIpcSubscriber<Dictionary<string, uint>, Dictionary<string, uint>>("ExtraChat.ChannelCommandColours");
ChannelNamesGate = Plugin.Interface.GetIpcSubscriber<Dictionary<Guid, string>, Dictionary<Guid, string>>("ExtraChat.ChannelNames");
+1 -1
View File
@@ -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,
+26 -69
View File
@@ -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<Chunk> Sender { get; }
public List<Chunk> 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<Chunk> Sender { get; }
internal List<Chunk> 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<Guid, float?> Height { get; } = new();
internal Dictionary<Guid, bool> IsVisible { get; } = new();
public int Hash { get; }
public Dictionary<Guid, float?> Height { get; } = new();
public Dictionary<Guid, bool> IsVisible { get; } = new();
internal Message(ulong receiver, ulong contentId, ulong accountId, ChatCode code, List<Chunk> sender, List<Chunk> content, SeString senderSource, SeString contentSource)
public Message(ulong receiver, ulong contentId, ulong accountId, ChatCode code, List<Chunk> sender, List<Chunk> 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<Chunk> sender, List<Chunk> content, SeString senderSource, SeString contentSource, SortCode sortCode, Guid extraChatChannel)
public Message(Guid id, ulong receiver, ulong contentId, DateTimeOffset date, ChatCode code, List<Chunk> sender, List<Chunk> 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<Chunk> content, ChatCode code)
public static Message FakeMessage(List<Chunk> content, ChatCode code)
{
return new Message(0, 0, 0, code, [], content, new SeString(), new SeString());
}
internal bool Matches(Dictionary<ChatType, ChatSource> channels, bool allExtraChatChannels, HashSet<Guid> extraChatChannels)
public bool Matches(Dictionary<ChatType, (ChatSource Source, ChatSource Target)> channels, bool allExtraChatChannels, HashSet<Guid> 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();
+18 -24
View File
@@ -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<Chunk>();
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;
}
}
+72 -32
View File
@@ -98,11 +98,11 @@ public class PayloadMessagePackFormatter : IMessagePackFormatter<Payload?>
}
}
public class SeStringMessagePackFormatter : IMessagePackFormatter<SeString>
public class SeStringMessagePackFormatter : IMessagePackFormatter<SeString?>
{
public void Serialize(ref MessagePackWriter writer, SeString value, MessagePackSerializerOptions options)
public void Serialize(ref MessagePackWriter writer, SeString? value, MessagePackSerializerOptions options)
{
options.Resolver.GetFormatter<List<Payload>>()!.Serialize(ref writer, value.Payloads, options);
options.Resolver.GetFormatter<List<Payload>>()!.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<uint> channels, ulong? receiver = null)
internal long CountDateRange(DateTime after, DateTime before, IEnumerable<byte> channels, ulong? receiver = null)
{
List<string> 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<uint> channels, ulong? receiver = null)
internal MessageEnumerator GetDateRange(DateTime after, DateTime before, IEnumerable<byte> channels, ulong? receiver = null)
{
List<string> 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<uint> channels, ulong? receiver = null, int page = 0)
internal MessageEnumerator GetPagedDateRange(DateTime after, DateTime before, IEnumerable<byte> channels, ulong? receiver = null, int page = 0)
{
List<string> 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<Message>, ID
(ulong)reader.GetInt64(1),
(ulong)reader.GetInt64(2),
DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64(3)),
new ChatCode((ushort)reader.GetInt32(4)),
MessagePackSerializer.Deserialize<List<Chunk>>(reader.GetFieldValue<byte[]>(5), MessageStore.MsgPackOptions),
MessagePackSerializer.Deserialize<List<Chunk>>(reader.GetFieldValue<byte[]>(6), MessageStore.MsgPackOptions),
MessagePackSerializer.Deserialize<SeString>(reader.GetFieldValue<byte[]>(7), MessageStore.MsgPackOptions),
MessagePackSerializer.Deserialize<SeString>(reader.GetFieldValue<byte[]>(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<List<Chunk>>(reader.GetFieldValue<byte[]>(7), MessageStore.MsgPackOptions),
MessagePackSerializer.Deserialize<List<Chunk>>(reader.GetFieldValue<byte[]>(8), MessageStore.MsgPackOptions),
MessagePackSerializer.Deserialize<SeString>(reader.GetFieldValue<byte[]>(9), MessageStore.MsgPackOptions),
MessagePackSerializer.Deserialize<SeString>(reader.GetFieldValue<byte[]>(10), MessageStore.MsgPackOptions),
reader.GetGuid(11)
);
}
catch (Exception e)
+7 -7
View File
@@ -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();
+50 -26
View File
@@ -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);
+81
View File
@@ -86,6 +86,33 @@ namespace ChatTwo.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to message is empty.
/// </summary>
internal static string ChatBox_Error_Empty {
get {
return ResourceManager.GetString("ChatBox_Error_Empty", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to message contained invalid characters.
/// </summary>
internal static string ChatBox_Error_Invalid {
get {
return ResourceManager.GetString("ChatBox_Error_Invalid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to message is longer than 500 bytes.
/// </summary>
internal static string ChatBox_Error_Too_Long {
get {
return ResourceManager.GetString("ChatBox_Error_Too_Long", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Input is disabled for this tab.
/// </summary>
@@ -1751,6 +1778,24 @@ namespace ChatTwo.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Source.
/// </summary>
internal static string ImGuiUtil_ChannelSelector_Source {
get {
return ResourceManager.GetString("ImGuiUtil_ChannelSelector_Source", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Target.
/// </summary>
internal static string ImGuiUtil_ChannelSelector_Target {
get {
return ResourceManager.GetString("ImGuiUtil_ChannelSelector_Target", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ESC to clear.
/// </summary>
@@ -2444,6 +2489,24 @@ namespace ChatTwo.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Target.
/// </summary>
internal static string Options_Header_Target {
get {
return ResourceManager.GetString("Options_Header_Target", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This disables the channel selection and shows all messages coming from the target..
/// </summary>
internal static string Options_Help_SenderMessages {
get {
return ResourceManager.GetString("Options_Help_SenderMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Hide the in-game chat window when the plugin is active..
/// </summary>
@@ -3398,6 +3461,15 @@ namespace ChatTwo.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Show all messages send by target.
/// </summary>
internal static string Options_Tabs_SenderMessages {
get {
return ResourceManager.GetString("Options_Tabs_SenderMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show timestamps.
/// </summary>
@@ -3713,6 +3785,15 @@ namespace ChatTwo.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Tell Exclusive.
/// </summary>
internal static string Tabs_Presets_Tell {
get {
return ResourceManager.GetString("Tabs_Presets_Tell", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to All.
/// </summary>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>Event</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Nova pestanya</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Disable input for this channel</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>Input is disabled for this tab</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Usage Notice</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -392,6 +392,9 @@ Sie wurden gewarnt.</value>
<data name="Tabs_Presets_Event">
<value>Ereignis</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Neuer Tab</value>
</data>
@@ -1145,6 +1148,9 @@ Sie wurden gewarnt.</value>
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Eingabe für diesen Kanal deaktivieren</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>Eingabe ist für diesen Tab deaktiviert</value>
</data>
@@ -1402,4 +1408,25 @@ Nachdem du 'Aktiviert' angeklickt und auf 'Start' gedrückt hast, wird die einge
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Nutzungshinweis</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>Evento</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Nueva pestaña</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Deshabilitar entrada para este canal</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>La entrada está deshabilitada para esta pestaña</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Aviso de Uso</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>Evénement</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Nouvel onglet</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Désactiver la saisie pour ce canal</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>La saisie est désactivée pour cet onglet</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Notice d'utilisation</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>Event</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Nuova tab</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Disable input for this channel</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>Input is disabled for this tab</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Avviso Di Utilizzo</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>イベント</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>新しいタブ</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>このチャンネルの入力を無効にする</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>このタブの入力は無効です</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>使用上の注意</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>이벤트</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>새 탭</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>이 탭에서 입력 비활성화</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>이 탭에서 입력이 비활성화된 상태입니다.</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Usage Notice</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>Evenement</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Nieuw Tabblad</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Invoer voor dit kanaal uitschakelen</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>Invoer is uitgeschakeld voor dit tabblad</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Gebruiksaanwijzing</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>Evento</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Nova aba</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Desativar a entrada para este canal</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>A entrada está desativada nesta aba</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Usage Notice</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>Event</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>New tab</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Disable input for this channel</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>Input is disabled for this tab</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Usage Notice</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>Eveniment</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Tab nou</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Disable input for this channel</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>Input is disabled for this tab</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Usage Notice</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>События</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Новая вкладка</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Disable input for this channel</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>Input is disabled for this tab</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Usage Notice</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>Event</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>Ny flik</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>Disable input for this channel</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>Input is disabled for this tab</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Usage Notice</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>剧情</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>新建标签页</value>
</data>
@@ -1144,6 +1147,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>禁用此频道的输入</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>此选项卡已禁用输入</value>
</data>
@@ -1399,4 +1405,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>使用须知</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+27
View File
@@ -391,6 +391,9 @@
<data name="Tabs_Presets_Event">
<value>劇情</value>
</data>
<data name="Tabs_Presets_Tell">
<value>Tell Exclusive</value>
</data>
<data name="Options_Tabs_NewTab">
<value>新建標籤頁</value>
</data>
@@ -1145,6 +1148,9 @@
<data name="Options_Tabs_NoInput" xml:space="preserve">
<value>禁用此頻道的輸入</value>
</data>
<data name="Options_Tabs_SenderMessages" xml:space="preserve">
<value>Show all messages send by target</value>
</data>
<data name="ChatLog_DisabledInput" xml:space="preserve">
<value>詞選項卡已禁用輸入</value>
</data>
@@ -1400,4 +1406,25 @@
<data name="Webinterface_UsageNotice" xml:space="preserve">
<value>Usage Notice</value>
</data>
<data name="Options_Help_SenderMessages" xml:space="preserve">
<value>This disables the channel selection and shows all messages coming from the target.</value>
</data>
<data name="Options_Header_Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="ChatBox_Error_Empty" xml:space="preserve">
<value>message is empty</value>
</data>
<data name="ChatBox_Error_Too_Long" xml:space="preserve">
<value>message is longer than 500 bytes</value>
</data>
<data name="ChatBox_Error_Invalid" xml:space="preserve">
<value>message contained invalid characters</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Source" xml:space="preserve">
<value>Source</value>
</data>
<data name="ImGuiUtil_ChannelSelector_Target" xml:space="preserve">
<value>Target</value>
</data>
</root>
+8 -3
View File
@@ -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<Item> ItemSheet;
public static readonly ExcelSheet<World> WorldSheet;
public static readonly ExcelSheet<Status> StatusSheet;
public static readonly ExcelSheet<UIColor> UIColorSheet;
public static readonly ExcelSheet<LogKind> LogKindSheet;
public static readonly ExcelSheet<LogFilter> LogFilterSheet;
public static readonly ExcelSheet<EventItem> EventItemSheet;
@@ -22,7 +22,6 @@ public static class Sheets
ItemSheet = Plugin.DataManager.GetExcelSheet<Item>();
WorldSheet = Plugin.DataManager.GetExcelSheet<World>();
StatusSheet = Plugin.DataManager.GetExcelSheet<Status>();
UIColorSheet = Plugin.DataManager.GetExcelSheet<UIColor>();
LogKindSheet = Plugin.DataManager.GetExcelSheet<LogKind>();
LogFilterSheet = Plugin.DataManager.GetExcelSheet<LogFilter>();
EventItemSheet = Plugin.DataManager.GetExcelSheet<EventItem>();
@@ -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<World> WorldsOnDatacenter(IPlayerCharacter character)
{
var dcRow = character.HomeWorld.Value.DataCenter.Value.Region.RowId;
return WorldSheet.Where(world => world.IsPublic && world.DataCenter.Value.Region.RowId == dcRow);
}
}
+17 -11
View File
@@ -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
+13 -13
View File
@@ -35,7 +35,7 @@ public class DbViewer : Window
private int CurrentPage = 1;
private string SimpleSearchTerm = "";
private bool OnlyCurrentCharacter = true;
private readonly Dictionary<ChatType, ChatSource> ChatCodes;
private readonly Dictionary<ChatType, (ChatSource, ChatSource)> 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)
+1 -1
View File
@@ -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;
+3 -2
View File
@@ -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
));
}
+5 -8
View File
@@ -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<ChatType, ChatSource>();
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);
}
+3 -3
View File
@@ -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));
+69 -9
View File
@@ -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<UnreadMode>())
{
@@ -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<InputChannel>())
if (ImGui.Selectable(channel.ToChatType().Name(), tab.Channel == channel))
tab.Channel = channel;
foreach (var channel in Enum.GetValues<InputChannel>())
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);
}
+41 -44
View File
@@ -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;
// }
}
+3 -4
View File
@@ -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);
}
+2
View File
@@ -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
};
+20 -8
View File
@@ -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<ChatType, ChatSource> chatCodes)
public static void ChannelSelector(string headerText, Dictionary<ChatType, (ChatSource Source, ChatSource Target)> 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<ChatSource>())
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<ChatSource>().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);
}
}
}
}
+118 -106
View File
@@ -3,135 +3,147 @@ using ChatTwo.Resources;
namespace ChatTwo.Util;
internal static class TabsUtil
public static class TabsUtil
{
internal static Dictionary<ChatType, ChatSource> AllChannels()
public static Dictionary<ChatType, (ChatSource, ChatSource)> AllChannels()
{
var channels = new Dictionary<ChatType, ChatSource>();
var channels = new Dictionary<ChatType, (ChatSource, ChatSource)>();
foreach (var chatType in Enum.GetValues<ChatType>())
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<ChatType, ChatSource>
SelectedChannels = new Dictionary<ChatType, (ChatSource, ChatSource)>
{
// 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, ChatSource> { [ChatType.NpcDialogue] = ChatSourceExt.All, },
SelectedChannels = new Dictionary<ChatType, (ChatSource, ChatSource)> { [ChatType.NpcDialogue] = (ChatSourceExt.All, ChatSourceExt.All), },
};
internal static Dictionary<ChatType, ChatSource> MostlyPlayer => new()
public static Tab VanillaTellExclusive => new()
{
Name = Language.Tabs_Presets_Tell,
SelectedChannels = new Dictionary<ChatType, (ChatSource, ChatSource)>
{
[ChatType.TellIncoming] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.TellOutgoing] = (ChatSourceExt.All, ChatSourceExt.All),
},
Channel = InputChannel.Tell,
AllSenderMessages = true,
};
public static Dictionary<ChatType, (ChatSource, ChatSource)> 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),
};
}
+3 -3
View File
@@ -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",