Merge branch 'main' into dean/extrachat-input-channels
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
tab_width = 4
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>1.20.0</Version>
|
<Version>1.20.3</Version>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|||||||
+20
-7
@@ -12,14 +12,14 @@ internal abstract class Chunk {
|
|||||||
internal Payload? Link { get; set; }
|
internal Payload? Link { get; set; }
|
||||||
|
|
||||||
protected Chunk(ChunkSource source, Payload? link) {
|
protected Chunk(ChunkSource source, Payload? link) {
|
||||||
this.Source = source;
|
Source = source;
|
||||||
this.Link = link;
|
Link = link;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal SeString? GetSeString() => this.Source switch {
|
internal SeString? GetSeString() => Source switch {
|
||||||
ChunkSource.None => null,
|
ChunkSource.None => null,
|
||||||
ChunkSource.Sender => this.Message?.SenderSource,
|
ChunkSource.Sender => Message?.SenderSource,
|
||||||
ChunkSource.Content => this.Message?.ContentSource,
|
ChunkSource.Content => Message?.ContentSource,
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,20 +52,33 @@ internal class TextChunk : Chunk {
|
|||||||
internal string Content { get; set; }
|
internal string Content { get; set; }
|
||||||
|
|
||||||
internal TextChunk(ChunkSource source, Payload? link, string content) : base(source, link) {
|
internal TextChunk(ChunkSource source, Payload? link, string content) : base(source, link) {
|
||||||
this.Content = content;
|
Content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CS8618
|
#pragma warning disable CS8618
|
||||||
public TextChunk() : base(ChunkSource.None, null) {
|
public TextChunk() : base(ChunkSource.None, null) {
|
||||||
}
|
}
|
||||||
#pragma warning restore CS8618
|
#pragma warning restore CS8618
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new TextChunk with identical styling to this one.
|
||||||
|
/// </summary>
|
||||||
|
public TextChunk NewWithStyle(ChunkSource source, Payload? link, string content)
|
||||||
|
{
|
||||||
|
return new TextChunk(source, link, content) {
|
||||||
|
FallbackColour = FallbackColour,
|
||||||
|
Foreground = Foreground,
|
||||||
|
Glow = Glow,
|
||||||
|
Italic = Italic,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class IconChunk : Chunk {
|
internal class IconChunk : Chunk {
|
||||||
internal BitmapFontIcon Icon { get; set; }
|
internal BitmapFontIcon Icon { get; set; }
|
||||||
|
|
||||||
public IconChunk(ChunkSource source, Payload? link, BitmapFontIcon icon) : base(source, link) {
|
public IconChunk(ChunkSource source, Payload? link, BitmapFontIcon icon) : base(source, link) {
|
||||||
this.Icon = icon;
|
Icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IconChunk() : base(ChunkSource.None, null) {
|
public IconChunk() : base(ChunkSource.None, null) {
|
||||||
|
|||||||
+12
-12
@@ -10,24 +10,24 @@ internal class ChatCode {
|
|||||||
internal ChatType Type { get; }
|
internal ChatType Type { get; }
|
||||||
internal ChatSource Source { get; }
|
internal ChatSource Source { get; }
|
||||||
internal ChatSource Target { get; }
|
internal ChatSource Target { get; }
|
||||||
private ChatSource SourceFrom(ushort shift) => (ChatSource) (1 << ((this.Raw >> shift) & 0xF));
|
private ChatSource SourceFrom(ushort shift) => (ChatSource) (1 << ((Raw >> shift) & 0xF));
|
||||||
|
|
||||||
internal ChatCode(ushort raw) {
|
internal ChatCode(ushort raw) {
|
||||||
this.Raw = raw;
|
Raw = raw;
|
||||||
this.Type = (ChatType) (this.Raw & Clear7);
|
Type = (ChatType) (Raw & Clear7);
|
||||||
this.Source = this.SourceFrom(11);
|
Source = SourceFrom(11);
|
||||||
this.Target = this.SourceFrom(7);
|
Target = SourceFrom(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BsonCtor]
|
[BsonCtor]
|
||||||
public ChatCode(ushort raw, ChatType type, ChatSource source, ChatSource target) {
|
public ChatCode(ushort raw, ChatType type, ChatSource source, ChatSource target) {
|
||||||
this.Raw = raw;
|
Raw = raw;
|
||||||
this.Type = type;
|
Type = type;
|
||||||
this.Source = source;
|
Source = source;
|
||||||
this.Target = target;
|
Target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ChatType Parent() => this.Type switch {
|
internal ChatType Parent() => Type switch {
|
||||||
ChatType.Say => ChatType.Say,
|
ChatType.Say => ChatType.Say,
|
||||||
ChatType.GmSay => ChatType.Say,
|
ChatType.GmSay => ChatType.Say,
|
||||||
ChatType.Shout => ChatType.Shout,
|
ChatType.Shout => ChatType.Shout,
|
||||||
@@ -81,11 +81,11 @@ internal class ChatCode {
|
|||||||
ChatType.FreeCompanyLoginLogout => ChatType.FreeCompanyAnnouncement,
|
ChatType.FreeCompanyLoginLogout => ChatType.FreeCompanyAnnouncement,
|
||||||
ChatType.PvpTeamAnnouncement => ChatType.PvpTeamAnnouncement,
|
ChatType.PvpTeamAnnouncement => ChatType.PvpTeamAnnouncement,
|
||||||
ChatType.PvpTeamLoginLogout => ChatType.PvpTeamAnnouncement,
|
ChatType.PvpTeamLoginLogout => ChatType.PvpTeamAnnouncement,
|
||||||
_ => this.Type,
|
_ => Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
internal bool IsBattle() {
|
internal bool IsBattle() {
|
||||||
switch (this.Type) {
|
switch (Type) {
|
||||||
case ChatType.Damage:
|
case ChatType.Damage:
|
||||||
case ChatType.Miss:
|
case ChatType.Miss:
|
||||||
case ChatType.Action:
|
case ChatType.Action:
|
||||||
|
|||||||
+12
-12
@@ -7,18 +7,18 @@ internal sealed class Commands : IDisposable {
|
|||||||
private Dictionary<string, CommandWrapper> Registered { get; } = new();
|
private Dictionary<string, CommandWrapper> Registered { get; } = new();
|
||||||
|
|
||||||
internal Commands(Plugin plugin) {
|
internal Commands(Plugin plugin) {
|
||||||
this.Plugin = plugin;
|
Plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
foreach (var name in this.Registered.Keys) {
|
foreach (var name in Registered.Keys) {
|
||||||
Plugin.CommandManager.RemoveHandler(name);
|
Plugin.CommandManager.RemoveHandler(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Initialise() {
|
internal void Initialise() {
|
||||||
foreach (var wrapper in this.Registered.Values) {
|
foreach (var wrapper in Registered.Values) {
|
||||||
Plugin.CommandManager.AddHandler(wrapper.Name, new CommandInfo(this.Invoke) {
|
Plugin.CommandManager.AddHandler(wrapper.Name, new CommandInfo(Invoke) {
|
||||||
HelpMessage = wrapper.Description ?? string.Empty,
|
HelpMessage = wrapper.Description ?? string.Empty,
|
||||||
ShowInHelp = wrapper.ShowInHelp,
|
ShowInHelp = wrapper.ShowInHelp,
|
||||||
});
|
});
|
||||||
@@ -26,7 +26,7 @@ internal sealed class Commands : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal CommandWrapper Register(string name, string? description = null, bool? showInHelp = null) {
|
internal CommandWrapper Register(string name, string? description = null, bool? showInHelp = null) {
|
||||||
if (this.Registered.TryGetValue(name, out var wrapper)) {
|
if (Registered.TryGetValue(name, out var wrapper)) {
|
||||||
if (description != null) {
|
if (description != null) {
|
||||||
wrapper.Description = description;
|
wrapper.Description = description;
|
||||||
}
|
}
|
||||||
@@ -38,12 +38,12 @@ internal sealed class Commands : IDisposable {
|
|||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Registered[name] = new CommandWrapper(name, description, showInHelp ?? true);
|
Registered[name] = new CommandWrapper(name, description, showInHelp ?? true);
|
||||||
return this.Registered[name];
|
return Registered[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Invoke(string command, string arguments) {
|
private void Invoke(string command, string arguments) {
|
||||||
if (!this.Registered.TryGetValue(command, out var wrapper)) {
|
if (!Registered.TryGetValue(command, out var wrapper)) {
|
||||||
Plugin.Log.Warning($"Missing registration for command {command}");
|
Plugin.Log.Warning($"Missing registration for command {command}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -64,12 +64,12 @@ internal sealed class CommandWrapper {
|
|||||||
internal event Action<string, string>? Execute;
|
internal event Action<string, string>? Execute;
|
||||||
|
|
||||||
internal CommandWrapper(string name, string? description, bool showInHelp) {
|
internal CommandWrapper(string name, string? description, bool showInHelp) {
|
||||||
this.Name = name;
|
Name = name;
|
||||||
this.Description = description;
|
Description = description;
|
||||||
this.ShowInHelp = showInHelp;
|
ShowInHelp = showInHelp;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Invoke(string command, string arguments) {
|
internal void Invoke(string command, string arguments) {
|
||||||
this.Execute?.Invoke(command, arguments);
|
Execute?.Invoke(command, arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ internal class Configuration : IPluginConfiguration {
|
|||||||
public bool HideDuringCutscenes = true;
|
public bool HideDuringCutscenes = true;
|
||||||
public bool HideWhenNotLoggedIn = true;
|
public bool HideWhenNotLoggedIn = true;
|
||||||
public bool HideWhenUiHidden = true;
|
public bool HideWhenUiHidden = true;
|
||||||
|
public bool HideInLoadingScreens;
|
||||||
public bool NativeItemTooltips = true;
|
public bool NativeItemTooltips = true;
|
||||||
public bool PrettierTimestamps = true;
|
public bool PrettierTimestamps = true;
|
||||||
public bool MoreCompactPretty;
|
public bool MoreCompactPretty;
|
||||||
@@ -50,6 +51,9 @@ internal class Configuration : IPluginConfiguration {
|
|||||||
public Dictionary<ChatType, uint> ChatColours = new();
|
public Dictionary<ChatType, uint> ChatColours = new();
|
||||||
public List<Tab> Tabs = new();
|
public List<Tab> Tabs = new();
|
||||||
|
|
||||||
|
public bool OverrideStyle = false;
|
||||||
|
public string ChosenStyle = "";
|
||||||
|
|
||||||
public uint DatabaseMigration = LatestDbVersion;
|
public uint DatabaseMigration = LatestDbVersion;
|
||||||
|
|
||||||
internal void UpdateFrom(Configuration other) {
|
internal void UpdateFrom(Configuration other) {
|
||||||
@@ -57,6 +61,7 @@ internal class Configuration : IPluginConfiguration {
|
|||||||
HideDuringCutscenes = other.HideDuringCutscenes;
|
HideDuringCutscenes = other.HideDuringCutscenes;
|
||||||
HideWhenNotLoggedIn = other.HideWhenNotLoggedIn;
|
HideWhenNotLoggedIn = other.HideWhenNotLoggedIn;
|
||||||
HideWhenUiHidden = other.HideWhenUiHidden;
|
HideWhenUiHidden = other.HideWhenUiHidden;
|
||||||
|
HideInLoadingScreens = other.HideInLoadingScreens;
|
||||||
NativeItemTooltips = other.NativeItemTooltips;
|
NativeItemTooltips = other.NativeItemTooltips;
|
||||||
PrettierTimestamps = other.PrettierTimestamps;
|
PrettierTimestamps = other.PrettierTimestamps;
|
||||||
MoreCompactPretty = other.MoreCompactPretty;
|
MoreCompactPretty = other.MoreCompactPretty;
|
||||||
@@ -88,6 +93,8 @@ internal class Configuration : IPluginConfiguration {
|
|||||||
ChatColours = other.ChatColours.ToDictionary(entry => entry.Key, entry => entry.Value);
|
ChatColours = other.ChatColours.ToDictionary(entry => entry.Key, entry => entry.Value);
|
||||||
Tabs = other.Tabs.Select(t => t.Clone()).ToList();
|
Tabs = other.Tabs.Select(t => t.Clone()).ToList();
|
||||||
DatabaseMigration = other.DatabaseMigration;
|
DatabaseMigration = other.DatabaseMigration;
|
||||||
|
OverrideStyle = other.OverrideStyle;
|
||||||
|
ChosenStyle = other.ChosenStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Migrate() {
|
public void Migrate() {
|
||||||
|
|||||||
@@ -39,21 +39,21 @@ internal sealed unsafe class Context {
|
|||||||
private Plugin Plugin { get; }
|
private Plugin Plugin { get; }
|
||||||
|
|
||||||
internal Context(Plugin plugin) {
|
internal Context(Plugin plugin) {
|
||||||
this.Plugin = plugin;
|
Plugin = plugin;
|
||||||
Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InviteToNoviceNetwork(string name, ushort world) {
|
internal void InviteToNoviceNetwork(string name, ushort world) {
|
||||||
if (this._inviteToNoviceNetwork == null) {
|
if (_inviteToNoviceNetwork == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6.3: 221EFD
|
// 6.3: 221EFD
|
||||||
var a1 = this.Plugin.Functions.GetInfoProxyByIndex(0x14);
|
var a1 = Plugin.Functions.GetInfoProxyByIndex(0x14);
|
||||||
|
|
||||||
fixed (byte* namePtr = name.ToTerminatedBytes()) {
|
fixed (byte* namePtr = name.ToTerminatedBytes()) {
|
||||||
// can specify content id if we have it, but there's no need
|
// can specify content id if we have it, but there's no need
|
||||||
this._inviteToNoviceNetwork(a1, 0, world, namePtr);
|
_inviteToNoviceNetwork(a1, 0, world, namePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,48 +66,48 @@ internal sealed unsafe class Context {
|
|||||||
// 0x10006: search recipes using this material
|
// 0x10006: search recipes using this material
|
||||||
|
|
||||||
internal void TryOn(uint itemId, byte stainId) {
|
internal void TryOn(uint itemId, byte stainId) {
|
||||||
if (this._tryOn == null) {
|
if (_tryOn == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._tryOn(0xFF, itemId, stainId, 0, 0);
|
_tryOn(0xFF, itemId, stainId, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void LinkItem(uint itemId) {
|
internal void LinkItem(uint itemId) {
|
||||||
if (this._linkItem == null) {
|
if (_linkItem == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog);
|
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog);
|
||||||
this._linkItem(agent, itemId);
|
_linkItem(agent, itemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OpenItemComparison(uint itemId) {
|
internal void OpenItemComparison(uint itemId) {
|
||||||
if (this._itemComparison == null) {
|
if (_itemComparison == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemCompare);
|
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemCompare);
|
||||||
this._itemComparison(agent, 0x4D, itemId, 0);
|
_itemComparison(agent, 0x4D, itemId, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SearchForRecipesUsingItem(uint itemId) {
|
internal void SearchForRecipesUsingItem(uint itemId) {
|
||||||
if (this._searchForRecipesUsingItem == null || this._searchForRecipesUsingItemVfunc is not { } offset) {
|
if (_searchForRecipesUsingItem == null || _searchForRecipesUsingItemVfunc is not { } offset) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var uiModule = Framework.Instance()->GetUiModule();
|
var uiModule = Framework.Instance()->GetUiModule();
|
||||||
var vf = (delegate* unmanaged<UIModule*, IntPtr>) uiModule->vfunc[offset / 8];
|
var vf = (delegate* unmanaged<UIModule*, IntPtr>) uiModule->vfunc[offset / 8];
|
||||||
var a1 = vf(uiModule);
|
var a1 = vf(uiModule);
|
||||||
this._searchForRecipesUsingItem(a1, itemId);
|
_searchForRecipesUsingItem(a1, itemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SearchForItem(uint itemId) {
|
internal void SearchForItem(uint itemId) {
|
||||||
if (this._searchForItem == null) {
|
if (_searchForItem == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemFinder = Framework.Instance()->GetUiModule()->GetItemFinderModule();
|
var itemFinder = Framework.Instance()->GetUiModule()->GetItemFinderModule();
|
||||||
this._searchForItem(itemFinder, itemId, 1);
|
_searchForItem(itemFinder, itemId, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,26 +66,26 @@ internal unsafe class GameFunctions : IDisposable {
|
|||||||
internal Context Context { get; }
|
internal Context Context { get; }
|
||||||
|
|
||||||
internal GameFunctions(Plugin plugin) {
|
internal GameFunctions(Plugin plugin) {
|
||||||
this.Plugin = plugin;
|
Plugin = plugin;
|
||||||
this.Party = new Party(this.Plugin);
|
Party = new Party(Plugin);
|
||||||
this.Chat = new Chat(this.Plugin);
|
Chat = new Chat(Plugin);
|
||||||
this.Context = new Context(this.Plugin);
|
Context = new Context(Plugin);
|
||||||
|
|
||||||
Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
||||||
|
|
||||||
this.ResolveTextCommandPlaceholderHook?.Enable();
|
ResolveTextCommandPlaceholderHook?.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
this.Chat.Dispose();
|
Chat.Dispose();
|
||||||
|
|
||||||
this.ResolveTextCommandPlaceholderHook?.Dispose();
|
ResolveTextCommandPlaceholderHook?.Dispose();
|
||||||
|
|
||||||
Marshal.FreeHGlobal(this._placeholderNamePtr);
|
Marshal.FreeHGlobal(_placeholderNamePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntPtr GetInfoModule() {
|
private IntPtr GetInfoModule() {
|
||||||
if (this._infoModuleVfunc is not { } vfunc) {
|
if (_infoModuleVfunc is not { } vfunc) {
|
||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,25 +95,25 @@ internal unsafe class GameFunctions : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal IntPtr GetInfoProxyByIndex(uint idx) {
|
internal IntPtr GetInfoProxyByIndex(uint idx) {
|
||||||
var infoModule = this.GetInfoModule();
|
var infoModule = GetInfoModule();
|
||||||
return infoModule == IntPtr.Zero ? IntPtr.Zero : this._getInfoProxyByIndex(infoModule, idx);
|
return infoModule == IntPtr.Zero ? IntPtr.Zero : _getInfoProxyByIndex(infoModule, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal uint? GetCurrentChatLogEntryIndex() {
|
internal uint? GetCurrentChatLogEntryIndex() {
|
||||||
if (this._currentChatEntryOffset == null) {
|
if (_currentChatEntryOffset == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var log = (IntPtr) Framework.Instance()->GetUiModule()->GetRaptureLogModule();
|
var log = (IntPtr) Framework.Instance()->GetUiModule()->GetRaptureLogModule();
|
||||||
return *(uint*) (log + this._currentChatEntryOffset.Value);
|
return *(uint*) (log + _currentChatEntryOffset.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SendFriendRequest(string name, ushort world) {
|
internal void SendFriendRequest(string name, ushort world) {
|
||||||
this.ListCommand(name, world, "friendlist");
|
ListCommand(name, world, "friendlist");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddToBlacklist(string name, ushort world) {
|
internal void AddToBlacklist(string name, ushort world) {
|
||||||
this.ListCommand(name, world, "blist");
|
ListCommand(name, world, "blist");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ListCommand(string name, ushort world, string commandName) {
|
private void ListCommand(string name, ushort world, string commandName) {
|
||||||
@@ -123,8 +123,8 @@ internal unsafe class GameFunctions : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var worldName = row.Name.RawString;
|
var worldName = row.Name.RawString;
|
||||||
this._replacementName = $"{name}@{worldName}";
|
_replacementName = $"{name}@{worldName}";
|
||||||
this.Plugin.Common.Functions.Chat.SendMessage($"/{commandName} add {this._placeholder}");
|
Plugin.Common.Functions.Chat.SendMessage($"/{commandName} add {_placeholder}");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void SetAddonInteractable(string name, bool interactable) {
|
internal static void SetAddonInteractable(string name, bool interactable) {
|
||||||
@@ -236,41 +236,41 @@ internal unsafe class GameFunctions : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal bool IsMentor() {
|
internal bool IsMentor() {
|
||||||
if (this._isMentor == null || this._isMentorA1 == null || this._isMentorA1.Value == IntPtr.Zero) {
|
if (_isMentor == null || _isMentorA1 == null || _isMentorA1.Value == IntPtr.Zero) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._isMentor(this._isMentorA1.Value) > 0;
|
return _isMentor(_isMentorA1.Value) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OpenPartyFinder(uint id) {
|
internal void OpenPartyFinder(uint id) {
|
||||||
if (this._openPartyFinder == null) {
|
if (_openPartyFinder == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.LookingForGroup);
|
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.LookingForGroup);
|
||||||
if (agent != null) {
|
if (agent != null) {
|
||||||
this._openPartyFinder(agent, id);
|
_openPartyFinder(agent, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OpenAchievement(uint id) {
|
internal void OpenAchievement(uint id) {
|
||||||
if (this._openAchievement == null) {
|
if (_openAchievement == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.Achievement);
|
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.Achievement);
|
||||||
if (agent != null) {
|
if (agent != null) {
|
||||||
this._openAchievement(agent, id);
|
_openAchievement(agent, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool IsInInstance() {
|
internal bool IsInInstance() {
|
||||||
if (this._inInstance == null) {
|
if (_inInstance == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._inInstance() != 0;
|
return _inInstance() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool TryOpenAdventurerPlate(ulong playerId)
|
internal bool TryOpenAdventurerPlate(ulong playerId)
|
||||||
@@ -304,21 +304,21 @@ internal unsafe class GameFunctions : IDisposable {
|
|||||||
private string? _replacementName;
|
private string? _replacementName;
|
||||||
|
|
||||||
private IntPtr ResolveTextCommandPlaceholderDetour(IntPtr a1, byte* placeholderText, byte a3, byte a4) {
|
private IntPtr ResolveTextCommandPlaceholderDetour(IntPtr a1, byte* placeholderText, byte a3, byte a4) {
|
||||||
if (this._replacementName == null) {
|
if (_replacementName == null) {
|
||||||
goto Original;
|
goto Original;
|
||||||
}
|
}
|
||||||
|
|
||||||
var placeholder = MemoryHelper.ReadStringNullTerminated((IntPtr) placeholderText);
|
var placeholder = MemoryHelper.ReadStringNullTerminated((IntPtr) placeholderText);
|
||||||
if (placeholder != this._placeholder) {
|
if (placeholder != _placeholder) {
|
||||||
goto Original;
|
goto Original;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryHelper.WriteString(this._placeholderNamePtr, this._replacementName);
|
MemoryHelper.WriteString(_placeholderNamePtr, _replacementName);
|
||||||
this._replacementName = null;
|
_replacementName = null;
|
||||||
|
|
||||||
return this._placeholderNamePtr;
|
return _placeholderNamePtr;
|
||||||
|
|
||||||
Original:
|
Original:
|
||||||
return this.ResolveTextCommandPlaceholderHook!.Original(a1, placeholderText, a3, a4);
|
return ResolveTextCommandPlaceholderHook!.Original(a1, placeholderText, a3, a4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,57 +25,57 @@ internal sealed unsafe class Party {
|
|||||||
private Plugin Plugin { get; }
|
private Plugin Plugin { get; }
|
||||||
|
|
||||||
internal Party(Plugin plugin) {
|
internal Party(Plugin plugin) {
|
||||||
this.Plugin = plugin;
|
Plugin = plugin;
|
||||||
Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
Plugin.GameInteropProvider.InitializeFromAttributes(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InviteSameWorld(string name, ushort world, ulong contentId) {
|
internal void InviteSameWorld(string name, ushort world, ulong contentId) {
|
||||||
if (this._inviteToParty == null) {
|
if (_inviteToParty == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6.11: 214A55
|
// 6.11: 214A55
|
||||||
var a1 = this.Plugin.Functions.GetInfoProxyByIndex(2);
|
var a1 = Plugin.Functions.GetInfoProxyByIndex(2);
|
||||||
fixed (byte* namePtr = name.ToTerminatedBytes()) {
|
fixed (byte* namePtr = name.ToTerminatedBytes()) {
|
||||||
// this only works if target is on the same world
|
// this only works if target is on the same world
|
||||||
this._inviteToParty(a1, contentId, namePtr, world);
|
_inviteToParty(a1, contentId, namePtr, world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InviteOtherWorld(ulong contentId) {
|
internal void InviteOtherWorld(ulong contentId) {
|
||||||
if (this._inviteToPartyContentId == null) {
|
if (_inviteToPartyContentId == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6.11: 214A55
|
// 6.11: 214A55
|
||||||
var a1 = this.Plugin.Functions.GetInfoProxyByIndex(2);
|
var a1 = Plugin.Functions.GetInfoProxyByIndex(2);
|
||||||
if (contentId != 0) {
|
if (contentId != 0) {
|
||||||
// third param is world, but it requires a specific world
|
// third param is world, but it requires a specific world
|
||||||
// if they're not on that world, it will fail
|
// if they're not on that world, it will fail
|
||||||
// pass 0 and it will work on any world EXCEPT for the world the
|
// pass 0 and it will work on any world EXCEPT for the world the
|
||||||
// current player is on
|
// current player is on
|
||||||
this._inviteToPartyContentId(a1, contentId, 0);
|
_inviteToPartyContentId(a1, contentId, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InviteInInstance(ulong contentId) {
|
internal void InviteInInstance(ulong contentId) {
|
||||||
if (this._inviteToPartyInInstance == null) {
|
if (_inviteToPartyInInstance == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6.11: 214A55
|
// 6.11: 214A55
|
||||||
var a1 = this.Plugin.Functions.GetInfoProxyByIndex(2);
|
var a1 = Plugin.Functions.GetInfoProxyByIndex(2);
|
||||||
if (contentId != 0) {
|
if (contentId != 0) {
|
||||||
// third param is world, but it requires a specific world
|
// third param is world, but it requires a specific world
|
||||||
// if they're not on that world, it will fail
|
// if they're not on that world, it will fail
|
||||||
// pass 0 and it will work on any world EXCEPT for the world the
|
// pass 0 and it will work on any world EXCEPT for the world the
|
||||||
// current player is on
|
// current player is on
|
||||||
this._inviteToPartyInInstance(a1, contentId);
|
_inviteToPartyInInstance(a1, contentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Kick(string name, ulong contentId) {
|
internal void Kick(string name, ulong contentId) {
|
||||||
if (this._kick == null) {
|
if (_kick == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,12 +85,12 @@ internal sealed unsafe class Party {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* namePtr = name.ToTerminatedBytes()) {
|
fixed (byte* namePtr = name.ToTerminatedBytes()) {
|
||||||
this._kick(agent, namePtr, 0, contentId);
|
_kick(agent, namePtr, 0, contentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Promote(string name, ulong contentId) {
|
internal void Promote(string name, ulong contentId) {
|
||||||
if (this._promote == null) {
|
if (_promote == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ internal sealed unsafe class Party {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* namePtr = name.ToTerminatedBytes()) {
|
fixed (byte* namePtr = name.ToTerminatedBytes()) {
|
||||||
this._promote(agent, namePtr, 0, contentId);
|
_promote(agent, namePtr, 0, contentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ internal class ChannelSwitchInfo {
|
|||||||
internal string? Text { get; }
|
internal string? Text { get; }
|
||||||
|
|
||||||
internal ChannelSwitchInfo(InputChannel? channel, bool permanent = false, RotateMode rotate = RotateMode.None, string? text = null) {
|
internal ChannelSwitchInfo(InputChannel? channel, bool permanent = false, RotateMode rotate = RotateMode.None, string? text = null) {
|
||||||
this.Channel = channel;
|
Channel = channel;
|
||||||
this.Permanent = permanent;
|
Permanent = permanent;
|
||||||
this.Rotate = rotate;
|
Rotate = rotate;
|
||||||
this.Text = text;
|
Text = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ internal sealed class ChatActivatedArgs {
|
|||||||
internal TellTarget? TellTarget { get; init; }
|
internal TellTarget? TellTarget { get; init; }
|
||||||
|
|
||||||
internal ChatActivatedArgs(ChannelSwitchInfo channelSwitchInfo) {
|
internal ChatActivatedArgs(ChannelSwitchInfo channelSwitchInfo) {
|
||||||
this.ChannelSwitchInfo = channelSwitchInfo;
|
ChannelSwitchInfo = channelSwitchInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ internal sealed class TellHistoryInfo {
|
|||||||
internal ulong ContentId { get; }
|
internal ulong ContentId { get; }
|
||||||
|
|
||||||
internal TellHistoryInfo(string name, uint world, ulong contentId) {
|
internal TellHistoryInfo(string name, uint world, ulong contentId) {
|
||||||
this.Name = name;
|
Name = name;
|
||||||
this.World = world;
|
World = world;
|
||||||
this.ContentId = contentId;
|
ContentId = contentId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ internal sealed class TellTarget {
|
|||||||
internal TellReason Reason { get; }
|
internal TellReason Reason { get; }
|
||||||
|
|
||||||
internal TellTarget(string name, ushort world, ulong contentId, TellReason reason) {
|
internal TellTarget(string name, ushort world, ulong contentId, TellReason reason) {
|
||||||
this.Name = name;
|
Name = name;
|
||||||
this.World = world;
|
World = world;
|
||||||
this.ContentId = contentId;
|
ContentId = contentId;
|
||||||
this.Reason = reason;
|
Reason = reason;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-16
@@ -19,47 +19,47 @@ internal sealed class ExtraChat : IDisposable {
|
|||||||
internal (string, uint)? ChannelOverride { get; set; }
|
internal (string, uint)? ChannelOverride { get; set; }
|
||||||
|
|
||||||
private Dictionary<string, uint> ChannelCommandColoursInternal { get; set; } = new();
|
private Dictionary<string, uint> ChannelCommandColoursInternal { get; set; } = new();
|
||||||
internal IReadOnlyDictionary<string, uint> ChannelCommandColours => this.ChannelCommandColoursInternal;
|
internal IReadOnlyDictionary<string, uint> ChannelCommandColours => ChannelCommandColoursInternal;
|
||||||
|
|
||||||
private Dictionary<Guid, string> ChannelNamesInternal { get; set; } = new();
|
private Dictionary<Guid, string> ChannelNamesInternal { get; set; } = new();
|
||||||
internal IReadOnlyDictionary<Guid, string> ChannelNames => this.ChannelNamesInternal;
|
internal IReadOnlyDictionary<Guid, string> ChannelNames => ChannelNamesInternal;
|
||||||
|
|
||||||
internal ExtraChat(Plugin plugin) {
|
internal ExtraChat(Plugin plugin) {
|
||||||
this.Plugin = plugin;
|
Plugin = plugin;
|
||||||
|
|
||||||
this.OverrideChannelGate = Plugin.Interface.GetIpcSubscriber<OverrideInfo, object>("ExtraChat.OverrideChannelColour");
|
OverrideChannelGate = Plugin.Interface.GetIpcSubscriber<OverrideInfo, object>("ExtraChat.OverrideChannelColour");
|
||||||
this.ChannelCommandColoursGate = Plugin.Interface.GetIpcSubscriber<Dictionary<string, uint>, Dictionary<string, uint>>("ExtraChat.ChannelCommandColours");
|
ChannelCommandColoursGate = Plugin.Interface.GetIpcSubscriber<Dictionary<string, uint>, Dictionary<string, uint>>("ExtraChat.ChannelCommandColours");
|
||||||
this.ChannelNamesGate = Plugin.Interface.GetIpcSubscriber<Dictionary<Guid, string>, Dictionary<Guid, string>>("ExtraChat.ChannelNames");
|
ChannelNamesGate = Plugin.Interface.GetIpcSubscriber<Dictionary<Guid, string>, Dictionary<Guid, string>>("ExtraChat.ChannelNames");
|
||||||
|
|
||||||
this.OverrideChannelGate.Subscribe(this.OnOverrideChannel);
|
OverrideChannelGate.Subscribe(OnOverrideChannel);
|
||||||
this.ChannelCommandColoursGate.Subscribe(this.OnChannelCommandColours);
|
ChannelCommandColoursGate.Subscribe(OnChannelCommandColours);
|
||||||
this.ChannelNamesGate.Subscribe(this.OnChannelNames);
|
ChannelNamesGate.Subscribe(OnChannelNames);
|
||||||
try {
|
try {
|
||||||
this.ChannelCommandColoursInternal = this.ChannelCommandColoursGate.InvokeFunc(null!);
|
ChannelCommandColoursInternal = ChannelCommandColoursGate.InvokeFunc(null!);
|
||||||
this.ChannelNamesInternal = this.ChannelNamesGate.InvokeFunc(null!);
|
ChannelNamesInternal = ChannelNamesGate.InvokeFunc(null!);
|
||||||
} catch (Exception) {
|
} catch (Exception) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
this.OverrideChannelGate.Unsubscribe(this.OnOverrideChannel);
|
OverrideChannelGate.Unsubscribe(OnOverrideChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOverrideChannel(OverrideInfo info) {
|
private void OnOverrideChannel(OverrideInfo info) {
|
||||||
if (info.Channel == null) {
|
if (info.Channel == null) {
|
||||||
this.ChannelOverride = null;
|
ChannelOverride = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ChannelOverride = (info.Channel, info.Rgba);
|
ChannelOverride = (info.Channel, info.Rgba);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnChannelCommandColours(Dictionary<string, uint> obj) {
|
private void OnChannelCommandColours(Dictionary<string, uint> obj) {
|
||||||
this.ChannelCommandColoursInternal = obj;
|
ChannelCommandColoursInternal = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnChannelNames(Dictionary<Guid, string> obj) {
|
private void OnChannelNames(Dictionary<Guid, string> obj) {
|
||||||
this.ChannelNamesInternal = obj;
|
ChannelNamesInternal = obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-14
@@ -15,38 +15,38 @@ internal sealed class IpcManager : IDisposable {
|
|||||||
internal List<string> Registered { get; } = new();
|
internal List<string> Registered { get; } = new();
|
||||||
|
|
||||||
public IpcManager(DalamudPluginInterface pluginInterface) {
|
public IpcManager(DalamudPluginInterface pluginInterface) {
|
||||||
this.Interface = pluginInterface;
|
Interface = pluginInterface;
|
||||||
|
|
||||||
this.RegisterGate = this.Interface.GetIpcProvider<string>("ChatTwo.Register");
|
RegisterGate = Interface.GetIpcProvider<string>("ChatTwo.Register");
|
||||||
this.RegisterGate.RegisterFunc(this.Register);
|
RegisterGate.RegisterFunc(Register);
|
||||||
|
|
||||||
this.AvailableGate = this.Interface.GetIpcProvider<object?>("ChatTwo.Available");
|
AvailableGate = Interface.GetIpcProvider<object?>("ChatTwo.Available");
|
||||||
|
|
||||||
this.UnregisterGate = this.Interface.GetIpcProvider<string, object?>("ChatTwo.Unregister");
|
UnregisterGate = Interface.GetIpcProvider<string, object?>("ChatTwo.Unregister");
|
||||||
this.UnregisterGate.RegisterAction(this.Unregister);
|
UnregisterGate.RegisterAction(Unregister);
|
||||||
|
|
||||||
this.InvokeGate = this.Interface.GetIpcProvider<string, PlayerPayload?, ulong, Payload?, SeString?, SeString?, object?>("ChatTwo.Invoke");
|
InvokeGate = Interface.GetIpcProvider<string, PlayerPayload?, ulong, Payload?, SeString?, SeString?, object?>("ChatTwo.Invoke");
|
||||||
|
|
||||||
this.AvailableGate.SendMessage();
|
AvailableGate.SendMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Invoke(string id, PlayerPayload? sender, ulong contentId, Payload? payload, SeString? senderString, SeString? content) {
|
internal void Invoke(string id, PlayerPayload? sender, ulong contentId, Payload? payload, SeString? senderString, SeString? content) {
|
||||||
this.InvokeGate.SendMessage(id, sender, contentId, payload, senderString, content);
|
InvokeGate.SendMessage(id, sender, contentId, payload, senderString, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Register() {
|
private string Register() {
|
||||||
var id = Guid.NewGuid().ToString();
|
var id = Guid.NewGuid().ToString();
|
||||||
this.Registered.Add(id);
|
Registered.Add(id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Unregister(string id) {
|
private void Unregister(string id) {
|
||||||
this.Registered.Remove(id);
|
Registered.Remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
this.UnregisterGate.UnregisterFunc();
|
UnregisterGate.UnregisterFunc();
|
||||||
this.RegisterGate.UnregisterFunc();
|
RegisterGate.UnregisterFunc();
|
||||||
this.Registered.Clear();
|
Registered.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+131
-46
@@ -1,7 +1,9 @@
|
|||||||
using ChatTwo.Code;
|
using ChatTwo.Code;
|
||||||
|
using ChatTwo.Util;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace ChatTwo;
|
||||||
|
|
||||||
@@ -10,15 +12,15 @@ internal class SortCode {
|
|||||||
internal ChatSource Source { get; set; }
|
internal ChatSource Source { get; set; }
|
||||||
|
|
||||||
internal SortCode(ChatType type, ChatSource source) {
|
internal SortCode(ChatType type, ChatSource source) {
|
||||||
this.Type = type;
|
Type = type;
|
||||||
this.Source = source;
|
Source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SortCode() {
|
public SortCode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Equals(SortCode other) {
|
private bool Equals(SortCode other) {
|
||||||
return this.Type == other.Type && this.Source == other.Source;
|
return Type == other.Type && Source == other.Source;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object? obj) {
|
public override bool Equals(object? obj) {
|
||||||
@@ -30,12 +32,12 @@ internal class SortCode {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj.GetType() == this.GetType() && this.Equals((SortCode) obj);
|
return obj.GetType() == GetType() && Equals((SortCode) obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode() {
|
public override int GetHashCode() {
|
||||||
unchecked {
|
unchecked {
|
||||||
return ((int) this.Type * 397) ^ (int) this.Source;
|
return ((int) Type * 397) ^ (int) Source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,16 +68,16 @@ internal class Message {
|
|||||||
internal int Hash { get; }
|
internal int Hash { get; }
|
||||||
|
|
||||||
internal Message(ulong receiver, ChatCode code, List<Chunk> sender, List<Chunk> content, SeString senderSource, SeString contentSource) {
|
internal Message(ulong receiver, ChatCode code, List<Chunk> sender, List<Chunk> content, SeString senderSource, SeString contentSource) {
|
||||||
this.Receiver = receiver;
|
Receiver = receiver;
|
||||||
this.Date = DateTime.UtcNow;
|
Date = DateTime.UtcNow;
|
||||||
this.Code = code;
|
Code = code;
|
||||||
this.Sender = sender;
|
Sender = sender;
|
||||||
this.Content = content;
|
Content = ReplaceContentURLs(content);
|
||||||
this.SenderSource = senderSource;
|
SenderSource = senderSource;
|
||||||
this.ContentSource = contentSource;
|
ContentSource = contentSource;
|
||||||
this.SortCode = new SortCode(this.Code.Type, this.Code.Source);
|
SortCode = new SortCode(Code.Type, Code.Source);
|
||||||
this.ExtraChatChannel = this.ExtractExtraChatChannel();
|
ExtraChatChannel = ExtractExtraChatChannel();
|
||||||
this.Hash = this.GenerateHash();
|
Hash = GenerateHash();
|
||||||
|
|
||||||
foreach (var chunk in sender.Concat(content)) {
|
foreach (var chunk in sender.Concat(content)) {
|
||||||
chunk.Message = this;
|
chunk.Message = this;
|
||||||
@@ -83,52 +85,56 @@ internal class Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal Message(ObjectId id, ulong receiver, ulong contentId, DateTime date, BsonDocument code, BsonArray sender, BsonArray content, BsonValue senderSource, BsonValue contentSource, BsonDocument sortCode) {
|
internal Message(ObjectId id, ulong receiver, ulong contentId, DateTime date, BsonDocument code, BsonArray sender, BsonArray content, BsonValue senderSource, BsonValue contentSource, BsonDocument sortCode) {
|
||||||
this.Id = id;
|
Id = id;
|
||||||
this.Receiver = receiver;
|
Receiver = receiver;
|
||||||
this.ContentId = contentId;
|
ContentId = contentId;
|
||||||
this.Date = date;
|
Date = date;
|
||||||
this.Code = BsonMapper.Global.ToObject<ChatCode>(code);
|
Code = BsonMapper.Global.ToObject<ChatCode>(code);
|
||||||
this.Sender = BsonMapper.Global.Deserialize<List<Chunk>>(sender);
|
Sender = BsonMapper.Global.Deserialize<List<Chunk>>(sender);
|
||||||
this.Content = BsonMapper.Global.Deserialize<List<Chunk>>(content);
|
// Don't call ReplaceContentURLs here since we're loading the message
|
||||||
this.SenderSource = BsonMapper.Global.Deserialize<SeString>(senderSource);
|
// from the database and it should already have parsed URL data.
|
||||||
this.ContentSource = BsonMapper.Global.Deserialize<SeString>(contentSource);
|
Content = BsonMapper.Global.Deserialize<List<Chunk>>(content);
|
||||||
this.SortCode = BsonMapper.Global.ToObject<SortCode>(sortCode);
|
SenderSource = BsonMapper.Global.Deserialize<SeString>(senderSource);
|
||||||
this.ExtraChatChannel = this.ExtractExtraChatChannel();
|
ContentSource = BsonMapper.Global.Deserialize<SeString>(contentSource);
|
||||||
this.Hash = this.GenerateHash();
|
SortCode = BsonMapper.Global.ToObject<SortCode>(sortCode);
|
||||||
|
ExtraChatChannel = ExtractExtraChatChannel();
|
||||||
|
Hash = GenerateHash();
|
||||||
|
|
||||||
foreach (var chunk in this.Sender.Concat(this.Content)) {
|
foreach (var chunk in Sender.Concat(Content)) {
|
||||||
chunk.Message = this;
|
chunk.Message = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Message(ObjectId id, ulong receiver, ulong contentId, DateTime date, BsonDocument code, BsonArray sender, BsonArray content, BsonValue senderSource, BsonValue contentSource, BsonDocument sortCode, BsonValue extraChatChannel) {
|
internal Message(ObjectId id, ulong receiver, ulong contentId, DateTime date, BsonDocument code, BsonArray sender, BsonArray content, BsonValue senderSource, BsonValue contentSource, BsonDocument sortCode, BsonValue extraChatChannel) {
|
||||||
this.Id = id;
|
Id = id;
|
||||||
this.Receiver = receiver;
|
Receiver = receiver;
|
||||||
this.ContentId = contentId;
|
ContentId = contentId;
|
||||||
this.Date = date;
|
Date = date;
|
||||||
this.Code = BsonMapper.Global.ToObject<ChatCode>(code);
|
Code = BsonMapper.Global.ToObject<ChatCode>(code);
|
||||||
this.Sender = BsonMapper.Global.Deserialize<List<Chunk>>(sender);
|
Sender = BsonMapper.Global.Deserialize<List<Chunk>>(sender);
|
||||||
this.Content = BsonMapper.Global.Deserialize<List<Chunk>>(content);
|
// Don't call ReplaceContentURLs here since we're loading the message
|
||||||
this.SenderSource = BsonMapper.Global.Deserialize<SeString>(senderSource);
|
// from the database and it should already have parsed URL data.
|
||||||
this.ContentSource = BsonMapper.Global.Deserialize<SeString>(contentSource);
|
Content = BsonMapper.Global.Deserialize<List<Chunk>>(content);
|
||||||
this.SortCode = BsonMapper.Global.ToObject<SortCode>(sortCode);
|
SenderSource = BsonMapper.Global.Deserialize<SeString>(senderSource);
|
||||||
this.ExtraChatChannel = BsonMapper.Global.Deserialize<Guid>(extraChatChannel);
|
ContentSource = BsonMapper.Global.Deserialize<SeString>(contentSource);
|
||||||
this.Hash = this.GenerateHash();
|
SortCode = BsonMapper.Global.ToObject<SortCode>(sortCode);
|
||||||
|
ExtraChatChannel = BsonMapper.Global.Deserialize<Guid>(extraChatChannel);
|
||||||
|
Hash = GenerateHash();
|
||||||
|
|
||||||
foreach (var chunk in this.Sender.Concat(this.Content)) {
|
foreach (var chunk in Sender.Concat(Content)) {
|
||||||
chunk.Message = this;
|
chunk.Message = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GenerateHash() {
|
private int GenerateHash() {
|
||||||
return this.SortCode.GetHashCode()
|
return SortCode.GetHashCode()
|
||||||
^ this.ExtraChatChannel.GetHashCode()
|
^ ExtraChatChannel.GetHashCode()
|
||||||
^ string.Join("", this.Sender.Select(c => c.StringValue())).GetHashCode()
|
^ string.Join("", Sender.Select(c => c.StringValue())).GetHashCode()
|
||||||
^ string.Join("", this.Content.Select(c => c.StringValue())).GetHashCode();
|
^ string.Join("", Content.Select(c => c.StringValue())).GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Guid ExtractExtraChatChannel() {
|
private Guid ExtractExtraChatChannel() {
|
||||||
if (this.ContentSource.Payloads.Count > 0 && this.ContentSource.Payloads[0] is RawPayload raw) {
|
if (ContentSource.Payloads.Count > 0 && ContentSource.Payloads[0] is RawPayload raw) {
|
||||||
// this does an encode and clone every time it's accessed, so cache
|
// this does an encode and clone every time it's accessed, so cache
|
||||||
var data = raw.Data;
|
var data = raw.Data;
|
||||||
if (data[1] == 0x27 && data[2] == 18 && data[3] == 0x20) {
|
if (data[1] == 0x27 && data[2] == 18 && data[3] == 0x20) {
|
||||||
@@ -138,4 +144,83 @@ internal class Message {
|
|||||||
|
|
||||||
return Guid.Empty;
|
return Guid.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// URLRegex returns a regex object that matches URLs like:
|
||||||
|
/// - https://example.com
|
||||||
|
/// - http://example.com
|
||||||
|
/// - www.example.com
|
||||||
|
/// - https://sub.example.com
|
||||||
|
/// - example.com
|
||||||
|
/// - sub.example.com
|
||||||
|
///
|
||||||
|
/// It matches URLs with www. or https:// prefix, and also matches URLs
|
||||||
|
/// without a prefix on specific TLDs.
|
||||||
|
/// </summary>
|
||||||
|
private static Regex URLRegex = new(
|
||||||
|
@"((https?:\/\/|www\.)[a-z0-9-]+(\.[a-z0-9-]+)*|([a-z0-9-]+(\.[a-z0-9-]+)*\.(com|net|org|co|io|app)))(:[\d]{1,5})?(\/[^\s]+)?",
|
||||||
|
RegexOptions.Compiled | RegexOptions.IgnoreCase
|
||||||
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all URL strings in all TextChunks, splits the parent TextChunk
|
||||||
|
/// apart and inserts a new TextChunk with a URIPayload.
|
||||||
|
/// </summary>
|
||||||
|
private List<Chunk> ReplaceContentURLs(List<Chunk> content)
|
||||||
|
{
|
||||||
|
var newChunks = new List<Chunk>();
|
||||||
|
void AddChunkWithMessage(Chunk chunk) {
|
||||||
|
chunk.Message = this;
|
||||||
|
newChunks.Add(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var chunk in content)
|
||||||
|
{
|
||||||
|
// Use as is if it's not a text chunk or it already has a payload.
|
||||||
|
if (chunk is not TextChunk text || chunk.Link != null)
|
||||||
|
{
|
||||||
|
// No need to call AddChunkWithMessage here since the chunk
|
||||||
|
// already has the Message field set.
|
||||||
|
newChunks.Add(chunk);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all URLs with the regex and insert a new TextChunk with a
|
||||||
|
// URIPayload.
|
||||||
|
var matches = URLRegex.Matches(text.Content);
|
||||||
|
var remainderIndex = 0;
|
||||||
|
foreach (Match match in matches.Cast<Match>())
|
||||||
|
{
|
||||||
|
// Add the text before the URL.
|
||||||
|
if (match.Index > remainderIndex)
|
||||||
|
{
|
||||||
|
AddChunkWithMessage(text.NewWithStyle(chunk.Source, chunk.Link, text.Content[remainderIndex..match.Index]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the remainder index.
|
||||||
|
remainderIndex = match.Index + match.Length;
|
||||||
|
|
||||||
|
// Create a new TextChunk with a URIPayload for the URL text.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var link = URIPayload.ResolveURI(match.Value);
|
||||||
|
AddChunkWithMessage(text.NewWithStyle(chunk.Source, link, match.Value));
|
||||||
|
}
|
||||||
|
catch (UriFormatException)
|
||||||
|
{
|
||||||
|
Plugin.Log.Debug($"Invalid URL accepted by Regex but failed URI parsing: '{match.Value}'");
|
||||||
|
// If the URL is invalid, set the remainder index to the
|
||||||
|
// beginning of the match so it'll get included in the next
|
||||||
|
// regular text chunk.
|
||||||
|
remainderIndex = match.Index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the text after the last URL.
|
||||||
|
if (remainderIndex < text.Content.Length)
|
||||||
|
AddChunkWithMessage(text.NewWithStyle(chunk.Source, null, text.Content[remainderIndex..]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return newChunks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+89
-24
@@ -86,6 +86,11 @@ public sealed class PayloadHandler {
|
|||||||
drawn = true;
|
drawn = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case URIPayload uri: {
|
||||||
|
DrawUriPopup(uri);
|
||||||
|
drawn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextFooter(drawn, chunk);
|
ContextFooter(drawn, chunk);
|
||||||
@@ -100,11 +105,10 @@ public sealed class PayloadHandler {
|
|||||||
if (registered.Count == 0) {
|
if (registered.Count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
var contentId = chunk.Message?.ContentId ?? 0;
|
var contentId = chunk.Message?.ContentId ?? 0;
|
||||||
var sender = chunk.Message?.Sender
|
var sender = chunk.Message?.Sender.Select(c => c.Link).FirstOrDefault(p => p is PlayerPayload) as PlayerPayload;
|
||||||
.Select(chunk => chunk.Link)
|
|
||||||
.FirstOrDefault(chunk => chunk is PlayerPayload) as PlayerPayload;
|
|
||||||
|
|
||||||
if (ImGui.BeginMenu(Language.Context_Integrations)) {
|
if (ImGui.BeginMenu(Language.Context_Integrations)) {
|
||||||
var cursor = ImGui.GetCursorPos();
|
var cursor = ImGui.GetCursorPos();
|
||||||
@@ -127,13 +131,18 @@ public sealed class PayloadHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ContextFooter(bool separator, Chunk chunk) {
|
private void ContextFooter(bool didCustomContext, Chunk chunk) {
|
||||||
if (separator) {
|
if (didCustomContext) {
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
}
|
|
||||||
|
|
||||||
if (!ImGui.BeginMenu(Plugin.PluginName)) {
|
// Only place these menu items in a submenu if we've already drawn
|
||||||
return;
|
// custom context menu items based on the payload.
|
||||||
|
//
|
||||||
|
// It makes it much more convenient in the majority of cases to
|
||||||
|
// copy the message content without having to open a submenu.
|
||||||
|
if (!ImGui.BeginMenu(Plugin.PluginName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Context_ScreenshotMode, ref LogWindow.ScreenshotMode);
|
ImGui.Checkbox(Language.Context_ScreenshotMode, ref LogWindow.ScreenshotMode);
|
||||||
@@ -143,21 +152,16 @@ public sealed class PayloadHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (chunk.Message is { } message) {
|
if (chunk.Message is { } message) {
|
||||||
if (ImGui.BeginMenu(Language.Context_Copy)) {
|
if (ImGui.Selectable(Language.Context_Copy)) {
|
||||||
var text = message.Sender
|
ImGui.SetClipboardText(StringifyMessage(message, true));
|
||||||
.Concat(message.Content)
|
WrapperUtil.AddNotification(Language.Context_CopySuccess, NotificationType.Info);
|
||||||
.Where(chunk => chunk is TextChunk)
|
}
|
||||||
.Cast<TextChunk>()
|
|
||||||
.Select(text => text.Content)
|
// Only show a separate "Copy content" option if the message has
|
||||||
.Aggregate(string.Concat);
|
// Sender chunks so it doesn't show for system messages.
|
||||||
ImGui.InputTextMultiline(
|
if (message.Sender.Count > 0 && ImGui.Selectable(Language.Context_CopyContent)) {
|
||||||
"##chat2-copy",
|
ImGui.SetClipboardText(StringifyMessage(message, false));
|
||||||
ref text,
|
WrapperUtil.AddNotification(Language.Context_CopyContentSuccess, NotificationType.Info);
|
||||||
(uint) text.Length,
|
|
||||||
new Vector2(350, 100) * ImGuiHelpers.GlobalScale,
|
|
||||||
ImGuiInputTextFlags.ReadOnly
|
|
||||||
);
|
|
||||||
ImGui.EndMenu();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var col = ImGui.GetStyle().Colors[(int) ImGuiCol.TextDisabled];
|
var col = ImGui.GetStyle().Colors[(int) ImGuiCol.TextDisabled];
|
||||||
@@ -172,7 +176,20 @@ public sealed class PayloadHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndMenu();
|
if (didCustomContext) ImGui.EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string StringifyMessage(Message message, bool withSender = false)
|
||||||
|
{
|
||||||
|
if (message == null)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var chunks = withSender ? message.Sender.Concat(message.Content) : message.Content;
|
||||||
|
return chunks
|
||||||
|
.Where(chunk => chunk is TextChunk)
|
||||||
|
.Cast<TextChunk>()
|
||||||
|
.Select(text => text.Content)
|
||||||
|
.Aggregate(string.Concat);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) {
|
internal void Click(Chunk chunk, Payload? payload, ImGuiMouseButton button) {
|
||||||
@@ -215,6 +232,11 @@ public sealed class PayloadHandler {
|
|||||||
DoHover(() => HoverItem(item), hoverSize);
|
DoHover(() => HoverItem(item), hoverSize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case URIPayload uri:
|
||||||
|
{
|
||||||
|
DoHover(() => HoverURI(uri), hoverSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,6 +356,11 @@ public sealed class PayloadHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HoverURI(URIPayload uri) {
|
||||||
|
ImGui.TextUnformatted(string.Format(Language.Context_URLDomain, uri.Uri.Authority));
|
||||||
|
ImGuiUtil.WarningText(Language.Context_URLWarning);
|
||||||
|
}
|
||||||
|
|
||||||
private void LeftClickPayload(Chunk chunk, Payload? payload) {
|
private void LeftClickPayload(Chunk chunk, Payload? payload) {
|
||||||
switch (payload) {
|
switch (payload) {
|
||||||
case MapLinkPayload map: {
|
case MapLinkPayload map: {
|
||||||
@@ -372,6 +399,10 @@ public sealed class PayloadHandler {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case URIPayload uri: {
|
||||||
|
TryOpenURI(uri.Uri);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,4 +656,38 @@ public sealed class PayloadHandler {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawUriPopup(URIPayload uri)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(string.Format(Language.Context_URLDomain, uri.Uri.Authority));
|
||||||
|
ImGuiUtil.WarningText(Language.Context_URLWarning, false);
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
if (ImGui.Selectable(Language.Context_OpenInBrowser))
|
||||||
|
{
|
||||||
|
TryOpenURI(uri.Uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.Selectable(Language.Context_CopyLink))
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText(uri.Uri.ToString());
|
||||||
|
WrapperUtil.AddNotification(Language.Context_CopyLinkNotification, NotificationType.Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryOpenURI(Uri uri)
|
||||||
|
{
|
||||||
|
new Thread(() => {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Plugin.Log.Debug($"Opening URI {uri} in default browser");
|
||||||
|
Dalamud.Utility.Util.OpenLink(uri.ToString());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Plugin.Log.Error($"Error opening URI: {ex}");
|
||||||
|
WrapperUtil.AddNotification(Language.Context_OpenInBrowserError, NotificationType.Error);
|
||||||
|
}
|
||||||
|
}).Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-10
@@ -1,9 +1,11 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
using ChatTwo.Ipc;
|
using ChatTwo.Ipc;
|
||||||
using ChatTwo.Resources;
|
using ChatTwo.Resources;
|
||||||
using ChatTwo.Ui;
|
using ChatTwo.Ui;
|
||||||
using ChatTwo.Util;
|
using ChatTwo.Util;
|
||||||
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Game.ClientState.Objects;
|
using Dalamud.Game.ClientState.Objects;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
@@ -15,7 +17,8 @@ using XivCommon;
|
|||||||
namespace ChatTwo;
|
namespace ChatTwo;
|
||||||
|
|
||||||
// ReSharper disable once ClassNeverInstantiated.Global
|
// ReSharper disable once ClassNeverInstantiated.Global
|
||||||
public sealed class Plugin : IDalamudPlugin {
|
public sealed class Plugin : IDalamudPlugin
|
||||||
|
{
|
||||||
internal const string PluginName = "Chat 2";
|
internal const string PluginName = "Chat 2";
|
||||||
|
|
||||||
[PluginService] internal static IPluginLog Log { get; private set; } = null!;
|
[PluginService] internal static IPluginLog Log { get; private set; } = null!;
|
||||||
@@ -37,11 +40,14 @@ public sealed class Plugin : IDalamudPlugin {
|
|||||||
[PluginService] internal static INotificationManager Notification { 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 IAddonLifecycle AddonLifecycle { get; private set; } = null!;
|
||||||
|
|
||||||
public readonly WindowSystem WindowSystem = new(PluginName);
|
public const string Authors = "Infi, Anna";
|
||||||
|
public static readonly string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "Unknown";
|
||||||
|
|
||||||
|
public readonly WindowSystem WindowSystem = new(PluginName);
|
||||||
public SettingsWindow SettingsWindow { get; }
|
public SettingsWindow SettingsWindow { get; }
|
||||||
public ChatLogWindow ChatLogWindow { get; }
|
public ChatLogWindow ChatLogWindow { get; }
|
||||||
public CommandHelpWindow CommandHelpWindow { get; }
|
public CommandHelpWindow CommandHelpWindow { get; }
|
||||||
|
public SeStringDebugger SeStringDebugger { get; }
|
||||||
|
|
||||||
internal Configuration Config { get; }
|
internal Configuration Config { get; }
|
||||||
internal Commands Commands { get; }
|
internal Commands Commands { get; }
|
||||||
@@ -58,7 +64,8 @@ public sealed class Plugin : IDalamudPlugin {
|
|||||||
internal DateTime GameStarted { get; }
|
internal DateTime GameStarted { get; }
|
||||||
|
|
||||||
#pragma warning disable CS8618
|
#pragma warning disable CS8618
|
||||||
public Plugin() {
|
public Plugin()
|
||||||
|
{
|
||||||
GameStarted = Process.GetCurrentProcess().StartTime.ToUniversalTime();
|
GameStarted = Process.GetCurrentProcess().StartTime.ToUniversalTime();
|
||||||
|
|
||||||
Config = Interface.GetPluginConfig() as Configuration ?? new Configuration();
|
Config = Interface.GetPluginConfig() as Configuration ?? new Configuration();
|
||||||
@@ -81,10 +88,12 @@ public sealed class Plugin : IDalamudPlugin {
|
|||||||
ChatLogWindow = new ChatLogWindow(this);
|
ChatLogWindow = new ChatLogWindow(this);
|
||||||
SettingsWindow = new SettingsWindow(this);
|
SettingsWindow = new SettingsWindow(this);
|
||||||
CommandHelpWindow = new CommandHelpWindow(ChatLogWindow);
|
CommandHelpWindow = new CommandHelpWindow(ChatLogWindow);
|
||||||
|
SeStringDebugger = new SeStringDebugger(this);
|
||||||
|
|
||||||
WindowSystem.AddWindow(ChatLogWindow);
|
WindowSystem.AddWindow(ChatLogWindow);
|
||||||
WindowSystem.AddWindow(SettingsWindow);
|
WindowSystem.AddWindow(SettingsWindow);
|
||||||
WindowSystem.AddWindow(CommandHelpWindow);
|
WindowSystem.AddWindow(CommandHelpWindow);
|
||||||
|
WindowSystem.AddWindow(SeStringDebugger);
|
||||||
FontManager.BuildFonts();
|
FontManager.BuildFonts();
|
||||||
|
|
||||||
Interface.UiBuilder.DisableCutsceneUiHide = true;
|
Interface.UiBuilder.DisableCutsceneUiHide = true;
|
||||||
@@ -105,7 +114,8 @@ public sealed class Plugin : IDalamudPlugin {
|
|||||||
}
|
}
|
||||||
#pragma warning restore CS8618
|
#pragma warning restore CS8618
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose()
|
||||||
|
{
|
||||||
Interface.LanguageChanged -= LanguageChanged;
|
Interface.LanguageChanged -= LanguageChanged;
|
||||||
Interface.UiBuilder.Draw -= Draw;
|
Interface.UiBuilder.Draw -= Draw;
|
||||||
Framework.Update -= FrameworkUpdate;
|
Framework.Update -= FrameworkUpdate;
|
||||||
@@ -114,6 +124,7 @@ public sealed class Plugin : IDalamudPlugin {
|
|||||||
WindowSystem.RemoveAllWindows();
|
WindowSystem.RemoveAllWindows();
|
||||||
ChatLogWindow.Dispose();
|
ChatLogWindow.Dispose();
|
||||||
SettingsWindow.Dispose();
|
SettingsWindow.Dispose();
|
||||||
|
SeStringDebugger.Dispose();
|
||||||
|
|
||||||
ExtraChat.Dispose();
|
ExtraChat.Dispose();
|
||||||
Ipc.Dispose();
|
Ipc.Dispose();
|
||||||
@@ -126,6 +137,10 @@ public sealed class Plugin : IDalamudPlugin {
|
|||||||
|
|
||||||
private void Draw()
|
private void Draw()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (Config.HideInLoadingScreens && Condition[ConditionFlag.BetweenAreas])
|
||||||
|
return;
|
||||||
|
|
||||||
Interface.UiBuilder.DisableUserUiHide = !Config.HideWhenUiHidden;
|
Interface.UiBuilder.DisableUserUiHide = !Config.HideWhenUiHidden;
|
||||||
ChatLogWindow.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text];
|
ChatLogWindow.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text];
|
||||||
|
|
||||||
@@ -135,11 +150,13 @@ public sealed class Plugin : IDalamudPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SaveConfig() {
|
internal void SaveConfig()
|
||||||
|
{
|
||||||
Interface.SavePluginConfig(Config);
|
Interface.SavePluginConfig(Config);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void LanguageChanged(string langCode) {
|
internal void LanguageChanged(string langCode)
|
||||||
|
{
|
||||||
var info = Config.LanguageOverride is LanguageOverride.None
|
var info = Config.LanguageOverride is LanguageOverride.None
|
||||||
? new CultureInfo(langCode)
|
? new CultureInfo(langCode)
|
||||||
: new CultureInfo(Config.LanguageOverride.Code());
|
: new CultureInfo(Config.LanguageOverride.Code());
|
||||||
@@ -147,15 +164,17 @@ public sealed class Plugin : IDalamudPlugin {
|
|||||||
Language.Culture = info;
|
Language.Culture = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly string[] ChatAddonNames = {
|
private static readonly string[] ChatAddonNames =
|
||||||
|
[
|
||||||
"ChatLog",
|
"ChatLog",
|
||||||
"ChatLogPanel_0",
|
"ChatLogPanel_0",
|
||||||
"ChatLogPanel_1",
|
"ChatLogPanel_1",
|
||||||
"ChatLogPanel_2",
|
"ChatLogPanel_2",
|
||||||
"ChatLogPanel_3",
|
"ChatLogPanel_3"
|
||||||
};
|
];
|
||||||
|
|
||||||
private void FrameworkUpdate(IFramework framework) {
|
private void FrameworkUpdate(IFramework framework)
|
||||||
|
{
|
||||||
if (DeferredSaveFrames >= 0 && DeferredSaveFrames-- == 0) {
|
if (DeferredSaveFrames >= 0 && DeferredSaveFrames-- == 0) {
|
||||||
SaveConfig();
|
SaveConfig();
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+173
-2
@@ -1103,6 +1103,24 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Copy content.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Context_CopyContent {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Context_CopyContent", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Copied message content to clipboard.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Context_CopyContentSuccess {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Context_CopyContentSuccess", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Copy Item Name.
|
/// Looks up a localized string similar to Copy Item Name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1112,6 +1130,33 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Copy link to clipboard.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Context_CopyLink {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Context_CopyLink", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Copied link to clipboard.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Context_CopyLinkNotification {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Context_CopyLinkNotification", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Copied message to clipboard.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Context_CopySuccess {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Context_CopySuccess", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Hide chat.
|
/// Looks up a localized string similar to Hide chat.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1193,6 +1238,24 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Open link in browser.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Context_OpenInBrowser {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Context_OpenInBrowser", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Failed to open the link in the browser, please report this issue.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Context_OpenInBrowserError {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Context_OpenInBrowserError", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Promote.
|
/// Looks up a localized string similar to Promote.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1274,6 +1337,24 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to URL at {0}.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Context_URLDomain {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Context_URLDomain", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Only open URLs from websites you trust.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Context_URLWarning {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Context_URLWarning", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Chinese (full).
|
/// Looks up a localized string similar to Chinese (full).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1418,6 +1499,15 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Authors: .
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_About_Authors {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_About_Authors", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Click the button to the left to see what's being worked on and what's next..
|
/// Looks up a localized string similar to Click the button to the left to see what's being worked on and what's next..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1428,7 +1518,7 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Click the button to the left to help translate {0}..
|
/// Looks up a localized string similar to Help to translate: .
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Options_About_CrowdIn {
|
internal static string Options_About_CrowdIn {
|
||||||
get {
|
get {
|
||||||
@@ -1436,6 +1526,33 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Discord: .
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_About_Discord {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_About_Discord", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Get help in the discord thread: .
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_About_Discord_Thread {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_About_Discord_Thread", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Get help through github issues: .
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_About_Github_Issues {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_About_Github_Issues", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to {0} is a project to completely recreate the in-game chat and make it even better..
|
/// Looks up a localized string similar to {0} is a project to completely recreate the in-game chat and make it even better..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1463,6 +1580,15 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Version: .
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_About_Version {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_About_Version", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Allow moving chat.
|
/// Looks up a localized string similar to Allow moving chat.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1716,7 +1842,7 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Hide chat during cutscenes.
|
/// Looks up a localized string similar to Hide during cutscenes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string Options_HideDuringCutscenes_Name {
|
internal static string Options_HideDuringCutscenes_Name {
|
||||||
get {
|
get {
|
||||||
@@ -1724,6 +1850,24 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Hide {0} during loading screens..
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_HideInLoadingScreens_Description {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_HideInLoadingScreens_Description", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Hide during loading screens.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_HideInLoadingScreens_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_HideInLoadingScreens_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Hide timestamps when previous messages have the same timestamp..
|
/// Looks up a localized string similar to Hide timestamps when previous messages have the same timestamp..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1904,6 +2048,33 @@ namespace ChatTwo.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Override Style.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_OverrideStyle_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_OverrideStyle_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Override your selected dalamud style with a different one.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_OverrideStyle_Name_Desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_OverrideStyle_Name_Desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Styles.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_OverrideStyleDropdown_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_OverrideStyleDropdown_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Display messages in a more modern style..
|
/// Looks up a localized string similar to Display messages in a more modern style..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
<value>Hide the in-game chat window when the plugin is active.</value>
|
<value>Hide the in-game chat window when the plugin is active.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Options_HideDuringCutscenes_Name">
|
<data name="Options_HideDuringCutscenes_Name">
|
||||||
<value>Hide chat during cutscenes</value>
|
<value>Hide during cutscenes</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Options_HideDuringCutscenes_Description">
|
<data name="Options_HideDuringCutscenes_Description">
|
||||||
<value>Hide {0} during cutscenes.</value>
|
<value>Hide {0} during cutscenes.</value>
|
||||||
@@ -377,7 +377,7 @@
|
|||||||
<value>Click the button to the left to see what's being worked on and what's next.</value>
|
<value>Click the button to the left to see what's being worked on and what's next.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Options_About_CrowdIn">
|
<data name="Options_About_CrowdIn">
|
||||||
<value>Click the button to the left to help translate {0}.</value>
|
<value>Help to translate: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Options_About_Translators">
|
<data name="Options_About_Translators">
|
||||||
<value>Translators</value>
|
<value>Translators</value>
|
||||||
@@ -502,6 +502,12 @@
|
|||||||
<data name="Options_SortAutoTranslate_Description">
|
<data name="Options_SortAutoTranslate_Description">
|
||||||
<value>If this is enabled, the Auto Translate list will be sorted alphabetically.</value>
|
<value>If this is enabled, the Auto Translate list will be sorted alphabetically.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Options_OverrideStyle_Name">
|
||||||
|
<value>Override Style</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_OverrideStyleDropdown_Name">
|
||||||
|
<value>Styles</value>
|
||||||
|
</data>
|
||||||
<data name="AutoTranslate_Completion_Key">
|
<data name="AutoTranslate_Completion_Key">
|
||||||
<value>Ctrl + {0}</value>
|
<value>Ctrl + {0}</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -859,16 +865,16 @@
|
|||||||
<data name="Migration_Line4">
|
<data name="Migration_Line4">
|
||||||
<value>Do not close FFXIV, unload Dalamud, or turn off your computer during this time.</value>
|
<value>Do not close FFXIV, unload Dalamud, or turn off your computer during this time.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Context_AdventurerPlate" xml:space="preserve">
|
<data name="Context_AdventurerPlate">
|
||||||
<value>View Adventurer Plate</value>
|
<value>View Adventurer Plate</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Context_AdventurerPlateError" xml:space="preserve">
|
<data name="Context_AdventurerPlateError">
|
||||||
<value>Unable to open adventurer plate at this moment</value>
|
<value>Unable to open adventurer plate at this moment</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Options_TooltipOffset_Name" xml:space="preserve">
|
<data name="Options_TooltipOffset_Name">
|
||||||
<value>Tooltip offset </value>
|
<value>Tooltip offset </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Options_TooltipOffset_Desc" xml:space="preserve">
|
<data name="Options_TooltipOffset_Desc">
|
||||||
<value>Use this option if you experience cut-off tooltips.</value>
|
<value>Use this option if you experience cut-off tooltips.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ChatType_ExtraChatLinkshell1" xml:space="preserve">
|
<data name="ChatType_ExtraChatLinkshell1" xml:space="preserve">
|
||||||
@@ -895,4 +901,55 @@
|
|||||||
<data name="ChatType_ExtraChatLinkshell8" xml:space="preserve">
|
<data name="ChatType_ExtraChatLinkshell8" xml:space="preserve">
|
||||||
<value>ExtraChat Linkshell [8]</value>
|
<value>ExtraChat Linkshell [8]</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Context_CopyContent">
|
||||||
|
<value>Copy content</value>
|
||||||
|
</data>
|
||||||
|
<data name="Context_CopyContentSuccess">
|
||||||
|
<value>Copied message content to clipboard</value>
|
||||||
|
</data>
|
||||||
|
<data name="Context_CopySuccess">
|
||||||
|
<value>Copied message to clipboard</value>
|
||||||
|
</data>
|
||||||
|
<data name="Context_CopyLink" xml:space="preserve">
|
||||||
|
<value>Copy link to clipboard</value>
|
||||||
|
</data>
|
||||||
|
<data name="Context_CopyLinkNotification" xml:space="preserve">
|
||||||
|
<value>Copied link to clipboard</value>
|
||||||
|
</data>
|
||||||
|
<data name="Context_OpenInBrowser" xml:space="preserve">
|
||||||
|
<value>Open link in browser</value>
|
||||||
|
</data>
|
||||||
|
<data name="Context_OpenInBrowserError" xml:space="preserve">
|
||||||
|
<value>Failed to open the link in the browser, please report this issue</value>
|
||||||
|
</data>
|
||||||
|
<data name="Context_URLDomain" xml:space="preserve">
|
||||||
|
<value>URL at {0}</value>
|
||||||
|
</data>
|
||||||
|
<data name="Context_URLWarning" xml:space="preserve">
|
||||||
|
<value>Only open URLs from websites you trust</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_About_Authors" xml:space="preserve">
|
||||||
|
<value>Authors: </value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_About_Discord" xml:space="preserve">
|
||||||
|
<value>Discord: </value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_About_Version" xml:space="preserve">
|
||||||
|
<value>Version: </value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_About_Github_Issues" xml:space="preserve">
|
||||||
|
<value>Get help through github issues: </value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_About_Discord_Thread" xml:space="preserve">
|
||||||
|
<value>Get help in the discord thread: </value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_OverrideStyle_Name_Desc" xml:space="preserve">
|
||||||
|
<value>Override your selected dalamud style with a different one</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_HideInLoadingScreens_Name" xml:space="preserve">
|
||||||
|
<value>Hide during loading screens</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_HideInLoadingScreens_Description" xml:space="preserve">
|
||||||
|
<value>Hide {0} during loading screens.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ internal class Store : IDisposable {
|
|||||||
["Type"] = new("PartyFinder"),
|
["Type"] = new("PartyFinder"),
|
||||||
["Id"] = new(partyFinder.Id),
|
["Id"] = new(partyFinder.Id),
|
||||||
});
|
});
|
||||||
|
case URIPayload uri:
|
||||||
|
return new BsonDocument(new Dictionary<string, BsonValue> {
|
||||||
|
["Type"] = new("URI"),
|
||||||
|
["Uri"] = new(uri.Uri.ToString()),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return payload?.Encode();
|
return payload?.Encode();
|
||||||
@@ -99,6 +104,7 @@ internal class Store : IDisposable {
|
|||||||
return bson["Type"].AsString switch {
|
return bson["Type"].AsString switch {
|
||||||
"Achievement" => new AchievementPayload((uint) bson["Id"].AsInt64),
|
"Achievement" => new AchievementPayload((uint) bson["Id"].AsInt64),
|
||||||
"PartyFinder" => new PartyFinderPayload((uint) bson["Id"].AsInt64),
|
"PartyFinder" => new PartyFinderPayload((uint) bson["Id"].AsInt64),
|
||||||
|
"URI" => new URIPayload(new Uri(bson["Uri"].AsString)),
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -257,6 +263,7 @@ internal class Store : IDisposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public (SeString? Sender, SeString? Message) LastMessage = (null, null);
|
||||||
private void ChatMessage(XivChatType type, uint senderId, SeString sender, SeString message) {
|
private void ChatMessage(XivChatType type, uint senderId, SeString sender, SeString message) {
|
||||||
var chatCode = new ChatCode((ushort) type);
|
var chatCode = new ChatCode((ushort) type);
|
||||||
|
|
||||||
@@ -265,6 +272,7 @@ internal class Store : IDisposable {
|
|||||||
formatting = FormatFor(chatCode.Type);
|
formatting = FormatFor(chatCode.Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LastMessage = (sender, message);
|
||||||
var senderChunks = new List<Chunk>();
|
var senderChunks = new List<Chunk>();
|
||||||
if (formatting is { IsPresent: true }) {
|
if (formatting is { IsPresent: true }) {
|
||||||
senderChunks.Add(new TextChunk(ChunkSource.None, null, formatting.Before) {
|
senderChunks.Add(new TextChunk(ChunkSource.None, null, formatting.Before) {
|
||||||
|
|||||||
+17
-17
@@ -11,17 +11,17 @@ internal class TextureCache : IDisposable {
|
|||||||
private readonly Dictionary<(uint, bool), IDalamudTextureWrap> _statusIcons = new();
|
private readonly Dictionary<(uint, bool), IDalamudTextureWrap> _statusIcons = new();
|
||||||
private readonly Dictionary<(uint, bool), IDalamudTextureWrap> _eventItemIcons = new();
|
private readonly Dictionary<(uint, bool), IDalamudTextureWrap> _eventItemIcons = new();
|
||||||
|
|
||||||
internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> ItemIcons => this._itemIcons;
|
internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> ItemIcons => _itemIcons;
|
||||||
internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> StatusIcons => this._statusIcons;
|
internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> StatusIcons => _statusIcons;
|
||||||
internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> EventItemIcons => this._eventItemIcons;
|
internal IReadOnlyDictionary<(uint, bool), IDalamudTextureWrap> EventItemIcons => _eventItemIcons;
|
||||||
|
|
||||||
internal TextureCache(ITextureProvider textureProvider) {
|
internal TextureCache(ITextureProvider textureProvider) {
|
||||||
this.TextureProvider = textureProvider;
|
TextureProvider = textureProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
var allIcons = this.ItemIcons.Values
|
var allIcons = ItemIcons.Values
|
||||||
.Concat(this.StatusIcons.Values);
|
.Concat(StatusIcons.Values);
|
||||||
|
|
||||||
foreach (var tex in allIcons) {
|
foreach (var tex in allIcons) {
|
||||||
tex.Dispose();
|
tex.Dispose();
|
||||||
@@ -34,40 +34,40 @@ internal class TextureCache : IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tex = hq
|
var tex = hq
|
||||||
? this.TextureProvider.GetIcon(icon, ITextureProvider.IconFlags.ItemHighQuality)
|
? TextureProvider.GetIcon(icon, ITextureProvider.IconFlags.ItemHighQuality)
|
||||||
: this.TextureProvider.GetIcon(icon);
|
: TextureProvider.GetIcon(icon);
|
||||||
if (tex != null) {
|
if (tex != null) {
|
||||||
dict[(icon, hq)] = tex;
|
dict[(icon, hq)] = tex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddItem(Item item, bool hq) {
|
internal void AddItem(Item item, bool hq) {
|
||||||
this.AddIcon(this._itemIcons, item.Icon, hq);
|
AddIcon(_itemIcons, item.Icon, hq);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddStatus(Status status) {
|
internal void AddStatus(Status status) {
|
||||||
this.AddIcon(this._statusIcons, status.Icon);
|
AddIcon(_statusIcons, status.Icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddEventItem(EventItem item) {
|
internal void AddEventItem(EventItem item) {
|
||||||
this.AddIcon(this._eventItemIcons, item.Icon);
|
AddIcon(_eventItemIcons, item.Icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IDalamudTextureWrap? GetItem(Item item, bool hq = false) {
|
internal IDalamudTextureWrap? GetItem(Item item, bool hq = false) {
|
||||||
this.AddItem(item, hq);
|
AddItem(item, hq);
|
||||||
this.ItemIcons.TryGetValue((item.Icon, hq), out var icon);
|
ItemIcons.TryGetValue((item.Icon, hq), out var icon);
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IDalamudTextureWrap? GetStatus(Status status) {
|
internal IDalamudTextureWrap? GetStatus(Status status) {
|
||||||
this.AddStatus(status);
|
AddStatus(status);
|
||||||
this.StatusIcons.TryGetValue((status.Icon, false), out var icon);
|
StatusIcons.TryGetValue((status.Icon, false), out var icon);
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IDalamudTextureWrap? GetEventItem(EventItem item) {
|
internal IDalamudTextureWrap? GetEventItem(EventItem item) {
|
||||||
this.AddEventItem(item);
|
AddEventItem(item);
|
||||||
this.EventItemIcons.TryGetValue((item.Icon, false), out var icon);
|
EventItemIcons.TryGetValue((item.Icon, false), out var icon);
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ internal class AutoCompleteInfo {
|
|||||||
internal int EndPos { get; }
|
internal int EndPos { get; }
|
||||||
|
|
||||||
internal AutoCompleteInfo(string toComplete, int startPos, int endPos) {
|
internal AutoCompleteInfo(string toComplete, int startPos, int endPos) {
|
||||||
this.ToComplete = toComplete;
|
ToComplete = toComplete;
|
||||||
this.StartPos = startPos;
|
StartPos = startPos;
|
||||||
this.EndPos = endPos;
|
EndPos = endPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-18
@@ -13,6 +13,7 @@ using Dalamud.Game.Text.SeStringHandling;
|
|||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
|
using Dalamud.Interface.Style;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
@@ -75,12 +76,8 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
Plugin = plugin;
|
Plugin = plugin;
|
||||||
Salt = new Random().Next().ToString();
|
Salt = new Random().Next().ToString();
|
||||||
|
|
||||||
|
Size = new Vector2(500, 250);
|
||||||
SizeCondition = ImGuiCond.FirstUseEver;
|
SizeCondition = ImGuiCond.FirstUseEver;
|
||||||
SizeConstraints = new WindowSizeConstraints
|
|
||||||
{
|
|
||||||
MinimumSize = new Vector2(500, 250),
|
|
||||||
MaximumSize = new Vector2(float.MaxValue, float.MaxValue)
|
|
||||||
};
|
|
||||||
|
|
||||||
PayloadHandler = new PayloadHandler(this);
|
PayloadHandler = new PayloadHandler(this);
|
||||||
HandlerLender = new Lender<PayloadHandler>(() => new PayloadHandler(this));
|
HandlerLender = new Lender<PayloadHandler>(() => new PayloadHandler(this));
|
||||||
@@ -100,6 +97,24 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
Plugin.AddonLifecycle.RegisterListener(AddonEvent.PostRequestedUpdate, "ItemDetail", PayloadHandler.MoveTooltip);
|
Plugin.AddonLifecycle.RegisterListener(AddonEvent.PostRequestedUpdate, "ItemDetail", PayloadHandler.MoveTooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void PreDraw()
|
||||||
|
{
|
||||||
|
if (Plugin.Config.OverrideStyle)
|
||||||
|
{
|
||||||
|
var styles = StyleModel.GetConfiguredStyles();
|
||||||
|
styles?.First(style => style.Name.Equals(Plugin.Config.ChosenStyle)).Push();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostDraw()
|
||||||
|
{
|
||||||
|
if (Plugin.Config.OverrideStyle)
|
||||||
|
{
|
||||||
|
var styles = StyleModel.GetConfiguredStyles();
|
||||||
|
styles?.First(style => style.Name.Equals(Plugin.Config.ChosenStyle)).Pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
Plugin.AddonLifecycle.UnregisterListener(AddonEvent.PostRequestedUpdate, "ItemDetail", PayloadHandler.MoveTooltip);
|
Plugin.AddonLifecycle.UnregisterListener(AddonEvent.PostRequestedUpdate, "ItemDetail", PayloadHandler.MoveTooltip);
|
||||||
Plugin.ClientState.Logout -= Logout;
|
Plugin.ClientState.Logout -= Logout;
|
||||||
@@ -1292,10 +1307,8 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
|| cmd.Alias.RawString == command
|
|| cmd.Alias.RawString == command
|
||||||
|| cmd.ShortCommand.RawString == command
|
|| cmd.ShortCommand.RawString == command
|
||||||
|| cmd.ShortAlias.RawString == command);
|
|| cmd.ShortAlias.RawString == command);
|
||||||
if (cmd != null) {
|
if (cmd != null)
|
||||||
Plugin.CommandHelpWindow.UpdateContent(cmd);
|
Plugin.CommandHelpWindow.UpdateContent(cmd);
|
||||||
Plugin.CommandHelpWindow.IsOpen = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->EventFlag != ImGuiInputTextFlags.CallbackHistory) {
|
if (data->EventFlag != ImGuiInputTextFlags.CallbackHistory) {
|
||||||
@@ -1367,18 +1380,20 @@ public sealed class ChatLogWindow : Window, IUiComponent {
|
|||||||
|
|
||||||
private void DrawChunk(Chunk chunk, bool wrap = true, PayloadHandler? handler = null, float lineWidth = 0f) {
|
private void DrawChunk(Chunk chunk, bool wrap = true, PayloadHandler? handler = null, float lineWidth = 0f) {
|
||||||
if (chunk is IconChunk icon && _fontIcon != null) {
|
if (chunk is IconChunk icon && _fontIcon != null) {
|
||||||
var bounds = IconUtil.GetBounds((byte) icon.Icon);
|
var bounds = IconUtil.GfdFileView.TryGetEntry((uint) icon.Icon, out var entry);
|
||||||
if (bounds != null) {
|
if (!bounds)
|
||||||
var texSize = new Vector2(_fontIcon.Width, _fontIcon.Height);
|
return;
|
||||||
|
|
||||||
var sizeRatio = Plugin.Config.FontSize / bounds.Value.W;
|
var texSize = new Vector2(_fontIcon.Width, _fontIcon.Height);
|
||||||
var size = new Vector2(bounds.Value.Z, bounds.Value.W) * sizeRatio * ImGuiHelpers.GlobalScale;
|
|
||||||
|
|
||||||
var uv0 = new Vector2(bounds.Value.X, bounds.Value.Y - 2) / texSize;
|
var sizeRatio = Plugin.Config.FontSize / entry.Height;
|
||||||
var uv1 = new Vector2(bounds.Value.X + bounds.Value.Z, bounds.Value.Y - 2 + bounds.Value.W) / texSize;
|
var size = new Vector2(entry.Width, entry.Height) * sizeRatio * ImGuiHelpers.GlobalScale;
|
||||||
ImGui.Image(_fontIcon.ImGuiHandle, size, uv0, uv1);
|
|
||||||
ImGuiUtil.PostPayload(chunk, handler);
|
var uv0 = new Vector2(entry.Left, entry.Top + 170) * 2 / texSize;
|
||||||
}
|
var uv1 = new Vector2(entry.Left + entry.Width, entry.Top + entry.Height + 170) * 2 / texSize;
|
||||||
|
|
||||||
|
ImGui.Image(_fontIcon.ImGuiHandle, size, uv0, uv1);
|
||||||
|
ImGuiUtil.PostPayload(chunk, handler);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public class CommandHelpWindow : Window {
|
|||||||
ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.AlwaysAutoResize;
|
ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.AlwaysAutoResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets IsOpen to true if it should be drawn
|
||||||
public void UpdateContent(TextCommand command)
|
public void UpdateContent(TextCommand command)
|
||||||
{
|
{
|
||||||
Command = command;
|
Command = command;
|
||||||
@@ -36,6 +37,7 @@ public class CommandHelpWindow : Window {
|
|||||||
break;
|
break;
|
||||||
case CommandHelpSide.None:
|
case CommandHelpSide.None:
|
||||||
default:
|
default:
|
||||||
|
IsOpen = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +47,8 @@ public class CommandHelpWindow : Window {
|
|||||||
MinimumSize = new Vector2(width, 0),
|
MinimumSize = new Vector2(width, 0),
|
||||||
MaximumSize = LogWindow.LastWindowSize with { X = width }
|
MaximumSize = LogWindow.LastWindowSize with { X = width }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
IsOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
|
|||||||
+6
-6
@@ -169,7 +169,7 @@ internal sealed class FaceData {
|
|||||||
internal byte[] Data { get; }
|
internal byte[] Data { get; }
|
||||||
|
|
||||||
internal FaceData(byte[] data) {
|
internal FaceData(byte[] data) {
|
||||||
this.Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,8 +178,8 @@ internal sealed class FontData {
|
|||||||
internal FaceData? Italic { get; }
|
internal FaceData? Italic { get; }
|
||||||
|
|
||||||
internal FontData(FaceData regular, FaceData? italic) {
|
internal FontData(FaceData regular, FaceData? italic) {
|
||||||
this.Regular = regular;
|
Regular = regular;
|
||||||
this.Italic = italic;
|
Italic = italic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,8 +189,8 @@ internal sealed class Font {
|
|||||||
internal string ResourcePathItalic { get; }
|
internal string ResourcePathItalic { get; }
|
||||||
|
|
||||||
internal Font(string name, string resourcePath, string resourcePathItalic) {
|
internal Font(string name, string resourcePath, string resourcePathItalic) {
|
||||||
this.Name = name;
|
Name = name;
|
||||||
this.ResourcePath = resourcePath;
|
ResourcePath = resourcePath;
|
||||||
this.ResourcePathItalic = resourcePathItalic;
|
ResourcePathItalic = resourcePathItalic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-6
@@ -1,4 +1,5 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Dalamud.Interface.Style;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
@@ -16,16 +17,17 @@ internal class Popout : Window
|
|||||||
Tab = tab;
|
Tab = tab;
|
||||||
Idx = idx;
|
Idx = idx;
|
||||||
|
|
||||||
|
Size = new Vector2(350, 350);
|
||||||
SizeCondition = ImGuiCond.FirstUseEver;
|
SizeCondition = ImGuiCond.FirstUseEver;
|
||||||
SizeConstraints = new WindowSizeConstraints
|
|
||||||
{
|
|
||||||
MinimumSize = new Vector2(350, 350),
|
|
||||||
MaximumSize = new Vector2(float.MaxValue, float.MaxValue)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PreDraw()
|
public override void PreDraw()
|
||||||
{
|
{
|
||||||
|
if (ChatLogWindow.Plugin.Config.OverrideStyle)
|
||||||
|
{
|
||||||
|
var styles = StyleModel.GetConfiguredStyles();
|
||||||
|
styles?.First(style => style.Name.Equals(ChatLogWindow.Plugin.Config.ChosenStyle)).Push();
|
||||||
|
}
|
||||||
Flags = ImGuiWindowFlags.None;
|
Flags = ImGuiWindowFlags.None;
|
||||||
if (!ChatLogWindow.Plugin.Config.ShowPopOutTitleBar)
|
if (!ChatLogWindow.Plugin.Config.ShowPopOutTitleBar)
|
||||||
Flags |= ImGuiWindowFlags.NoTitleBar;
|
Flags |= ImGuiWindowFlags.NoTitleBar;
|
||||||
@@ -53,7 +55,13 @@ internal class Popout : Window
|
|||||||
|
|
||||||
public override void PostDraw()
|
public override void PostDraw()
|
||||||
{
|
{
|
||||||
|
|
||||||
ChatLogWindow.PopOutDocked[Idx] = ImGui.IsWindowDocked();
|
ChatLogWindow.PopOutDocked[Idx] = ImGui.IsWindowDocked();
|
||||||
|
if (ChatLogWindow.Plugin.Config.OverrideStyle)
|
||||||
|
{
|
||||||
|
var styles = StyleModel.GetConfiguredStyles();
|
||||||
|
styles?.First(style => style.Name.Equals(ChatLogWindow.Plugin.Config.ChosenStyle)).Pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnClose()
|
public override void OnClose()
|
||||||
@@ -64,4 +72,4 @@ internal class Popout : Window
|
|||||||
Tab.PopOut = false;
|
Tab.PopOut = false;
|
||||||
ChatLogWindow.Plugin.SaveConfig();
|
ChatLogWindow.Plugin.SaveConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,323 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using ChatTwo.Util;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
|
using Dalamud.Interface.Windowing;
|
||||||
|
using ImGuiNET;
|
||||||
|
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
|
||||||
|
|
||||||
|
namespace ChatTwo.Ui;
|
||||||
|
|
||||||
|
public class SeStringDebugger : Window
|
||||||
|
{
|
||||||
|
private readonly Plugin Plugin;
|
||||||
|
|
||||||
|
public SeStringDebugger(Plugin plugin) : base($"SeString Debugger###chat2-sestringdebugger")
|
||||||
|
{
|
||||||
|
Plugin = plugin;
|
||||||
|
|
||||||
|
SizeConstraints = new WindowSizeConstraints
|
||||||
|
{
|
||||||
|
MinimumSize = new Vector2(475, 600),
|
||||||
|
MaximumSize = new Vector2(float.MaxValue, float.MaxValue)
|
||||||
|
};
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Plugin.Commands.Register("/chat2Debugger").Execute += Toggle;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Plugin.Commands.Register("/chat2Debugger").Execute -= Toggle;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Toggle(string _, string __) => Toggle();
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("SeString Content");
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
if (Plugin.Store.LastMessage.Message == null)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("Nothing to show");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make SeString freely selectable through chat
|
||||||
|
foreach (var payload in Plugin.Store.LastMessage.Message.Payloads)
|
||||||
|
{
|
||||||
|
switch (payload)
|
||||||
|
{
|
||||||
|
case UIForegroundPayload color:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link ForegroundColor", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Enabled?", color.IsEnabled.ToString() },
|
||||||
|
{ "ColorKey", color.IsEnabled ? color.ColorKey.ToString() : "Color Ended" },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MapLinkPayload map:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link MapLinkPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Map.RowId", map.Map?.RowId.ToString() },
|
||||||
|
{ "Map.PlaceName", map.Map?.PlaceName.Value?.Name.ToString() },
|
||||||
|
{ "Map.PlaceNameRegion", map.Map?.PlaceNameRegion.Value?.Name.ToString() },
|
||||||
|
{ "Map.PlaceNameSub", map.Map?.PlaceNameSub.Value?.Name.ToString() },
|
||||||
|
{ "TerritoryType.RowId", map.TerritoryType?.RowId.ToString() },
|
||||||
|
{ "RawX", map.RawX.ToString() },
|
||||||
|
{ "RawY", map.RawY.ToString() },
|
||||||
|
{ "XCoord", map.XCoord.ToString() },
|
||||||
|
{ "YCoord", map.YCoord.ToString() },
|
||||||
|
{ "CoordinateString", map.CoordinateString },
|
||||||
|
{ "DataString", map.DataString },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QuestPayload quest:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link QuestPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Quest.RowId", quest.Quest?.RowId.ToString() },
|
||||||
|
{ "Quest.Name", quest.Quest?.Name.ToString() },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DalamudLinkPayload link:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link DalamudLinkPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "CommandId", link.CommandId.ToString() },
|
||||||
|
{ "Plugin", link.Plugin },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DalamudPartyFinderPayload pf:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link PartyFinderPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "ListingId", pf.ListingId.ToString() },
|
||||||
|
{ "LinkType", EnumName(pf.LinkType) },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PlayerPayload player:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link PlayerPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Real", player.DisplayedName },
|
||||||
|
{ "PlayerName", player.PlayerName },
|
||||||
|
{ "World.Name", player.World.Name },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ItemPayload item:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link ItemPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "ItemId", item.ItemId.ToString() },
|
||||||
|
{ "RawItemId", item.RawItemId.ToString() },
|
||||||
|
{ "Kind", EnumName(item.Kind) },
|
||||||
|
{ "IsHQ", item.IsHQ.ToString() },
|
||||||
|
{ "Item.Name", item.Item?.Name.ToString() },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AutoTranslatePayload at:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link AutoTranslatePayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Text", at.Text },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IconPayload icon:
|
||||||
|
{
|
||||||
|
var found = IconUtil.GfdFileView.TryGetEntry((uint) icon.Icon, out var entry);
|
||||||
|
RenderMetadataDictionary("Link IconPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Found", found.ToString() },
|
||||||
|
{ "Icon ID", ((uint) icon.Icon).ToString() },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RawPayload raw:
|
||||||
|
{
|
||||||
|
var colorPayload = ColorPayload.From(raw.Data);
|
||||||
|
if (colorPayload != null)
|
||||||
|
{
|
||||||
|
var push = colorPayload.Enabled && colorPayload.Color != 0;
|
||||||
|
// if (push) ImGui.PushStyleColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(colorPayload.U));
|
||||||
|
RenderMetadataDictionary("Link ColorPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Unshifted", colorPayload.UnshiftedColor.ToString("X8") },
|
||||||
|
{ "Color", colorPayload.Color.ToString("X8") },
|
||||||
|
{ "Enabled?", colorPayload.Enabled.ToString() },
|
||||||
|
});
|
||||||
|
// if (push) ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link RawPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Data", string.Join(" ", raw.Data.Select(b => b.ToString("X2"))) },
|
||||||
|
{ "Type", EnumName(raw.Type) },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StatusPayload status:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link StatusPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Status.RowId", status.Status.RowId.ToString() },
|
||||||
|
{ "Status.Name", status.Status.Name },
|
||||||
|
{ "Status.Icon", status.Status.Icon.ToString() }
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Util.PartyFinderPayload pf:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link PartyFinderPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Id", pf.Id.ToString() }
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AchievementPayload achievement:
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Link AchievementPayload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Id", achievement.Id.ToString() }
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
var payloadData = payload.Encode();
|
||||||
|
|
||||||
|
var initialByte = payloadData.First();
|
||||||
|
if (initialByte != 0x02)
|
||||||
|
{
|
||||||
|
RenderMetadataDictionary("Text Payload", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Content", Encoding.UTF8.GetString(payloadData) },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var unknown = new RawPayload(payloadData);
|
||||||
|
RenderMetadataDictionary("Link Unknown", new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "Unknown", string.Join(" ", unknown.Data.Select(b => b.ToString("X2"))) },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? EnumName<T>(T? value) where T : Enum
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var rawValue = Convert.ChangeType(value, value.GetTypeCode());
|
||||||
|
return (Enum.GetName(value.GetType(), value) ?? "Unknown") + $" ({rawValue})";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenderMetadataDictionary(string name, Dictionary<string, string?> metadata)
|
||||||
|
{
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
|
||||||
|
ImGui.Text($"{name}:");
|
||||||
|
ImGui.Indent(style.IndentSpacing);
|
||||||
|
if (!ImGui.BeginTable($"##chat3-{name}", 2, 0))
|
||||||
|
{
|
||||||
|
ImGui.EndTable();
|
||||||
|
ImGui.Unindent(style.IndentSpacing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ImGui.TableSetupColumn($"##chat3-{name}-key", 0, 0.4f);
|
||||||
|
ImGui.TableSetupColumn($"##chat3-{name}-value");
|
||||||
|
for (var i = 0; i < metadata.Count; i++)
|
||||||
|
{
|
||||||
|
var (key, value) = metadata.ElementAt(i);
|
||||||
|
ImGui.PushID(i);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.Text(key);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGuiTextVisibleWhitespace(value);
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
ImGui.EndTable();
|
||||||
|
ImGui.Unindent(style.IndentSpacing);
|
||||||
|
ImGui.NewLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImGuiTextVisibleWhitespace replaces leading and trailing whitespace with
|
||||||
|
// visible characters. The extra characters are rendered with a muted font.
|
||||||
|
private static void ImGuiTextVisibleWhitespace(string? original, bool wrap = true)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(original))
|
||||||
|
{
|
||||||
|
var str = original == null ? "(null)" : "(empty)";
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 1, 1, 0.5f));
|
||||||
|
ImGui.TextUnformatted(str);
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = original;
|
||||||
|
var start = 0;
|
||||||
|
var end = text.Length;
|
||||||
|
|
||||||
|
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0));
|
||||||
|
|
||||||
|
void WriteText(string text)
|
||||||
|
{
|
||||||
|
if (wrap)
|
||||||
|
{
|
||||||
|
ImGui.TextWrapped(text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (start < end && char.IsWhiteSpace(text[start]))
|
||||||
|
{
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
if (start > 0)
|
||||||
|
{
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 1, 1, 0.5f));
|
||||||
|
WriteText(new string('_', start));
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
ImGui.SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (end > start && char.IsWhiteSpace(text[end - 1]))
|
||||||
|
{
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteText(text[start..end]);
|
||||||
|
if (end < text.Length)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 1, 1, 0.5f));
|
||||||
|
WriteText(new string('_', text.Length - end));
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.PopStyleVar();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ using System.Numerics;
|
|||||||
using ChatTwo.Resources;
|
using ChatTwo.Resources;
|
||||||
using ChatTwo.Util;
|
using ChatTwo.Util;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace ChatTwo.Ui.SettingsTabs;
|
||||||
@@ -31,23 +33,40 @@ internal sealed class About : ISettingsTab {
|
|||||||
|
|
||||||
ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName));
|
ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName));
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(10.0f);
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(Language.Options_About_Authors);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextColored(ImGuiColors.ParsedGold, Plugin.Authors);
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(Language.Options_About_Discord);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextColored(ImGuiColors.ParsedGold, "@infi");
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(Language.Options_About_Version);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextColored(ImGuiColors.ParsedOrange, Plugin.Version);
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(10.0f);
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(Language.Options_About_Discord_Thread);
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "discordThread"))
|
||||||
|
Dalamud.Utility.Util.OpenLink("https://canary.discord.com/channels/581875019861328007/1224865018789761126");
|
||||||
|
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "clickup"))
|
ImGui.TextUnformatted(Language.Options_About_Github_Issues);
|
||||||
{
|
|
||||||
Dalamud.Utility.Util.OpenLink("https://sharing.clickup.com/b/h/6-122378074-2/1047d21a39a4140");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted(Language.Options_About_ClickUp);
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "githubIssues"))
|
||||||
|
Dalamud.Utility.Util.OpenLink("https://github.com/Infiziert90/ChatTwo/issues");
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(10.0f);
|
||||||
|
|
||||||
|
ImGui.TextUnformatted(Language.Options_About_CrowdIn);
|
||||||
|
ImGui.SameLine();
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "crowdin"))
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "crowdin"))
|
||||||
{
|
|
||||||
Dalamud.Utility.Util.OpenLink("https://crowdin.com/project/chattwo");
|
Dalamud.Utility.Util.OpenLink("https://crowdin.com/project/chattwo");
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(string.Format(Language.Options_About_CrowdIn, Plugin.PluginName));
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
@@ -68,7 +87,6 @@ internal sealed class About : ISettingsTab {
|
|||||||
ImGui.EndChild();
|
ImGui.EndChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiUtil.HelpText($"{Plugin.PluginName} v{GetType().Assembly.GetName().Version?.ToString(3)}");
|
|
||||||
ImGui.PopTextWrapPos();
|
ImGui.PopTextWrapPos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ internal sealed class ChatColours : ISettingsTab {
|
|||||||
public string Name => Language.Options_ChatColours_Tab + "###tabs-chat-colours";
|
public string Name => Language.Options_ChatColours_Tab + "###tabs-chat-colours";
|
||||||
|
|
||||||
internal ChatColours(Configuration mutable, Plugin plugin) {
|
internal ChatColours(Configuration mutable, Plugin plugin) {
|
||||||
this.Mutable = mutable;
|
Mutable = mutable;
|
||||||
this.Plugin = plugin;
|
Plugin = plugin;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
var sortable = ChatTypeExt.SortOrder
|
var sortable = ChatTypeExt.SortOrder
|
||||||
@@ -36,23 +36,23 @@ internal sealed class ChatColours : ISettingsTab {
|
|||||||
foreach (var (_, types) in ChatTypeExt.SortOrder) {
|
foreach (var (_, types) in ChatTypeExt.SortOrder) {
|
||||||
foreach (var type in types) {
|
foreach (var type in types) {
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.UndoAlt, $"{type}", Language.Options_ChatColours_Reset)) {
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.UndoAlt, $"{type}", Language.Options_ChatColours_Reset)) {
|
||||||
this.Mutable.ChatColours.Remove(type);
|
Mutable.ChatColours.Remove(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.LongArrowAltDown, $"{type}", Language.Options_ChatColours_Import)) {
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.LongArrowAltDown, $"{type}", Language.Options_ChatColours_Import)) {
|
||||||
var gameColour = this.Plugin.Functions.Chat.GetChannelColour(type);
|
var gameColour = Plugin.Functions.Chat.GetChannelColour(type);
|
||||||
this.Mutable.ChatColours[type] = gameColour ?? type.DefaultColour() ?? 0;
|
Mutable.ChatColours[type] = gameColour ?? type.DefaultColour() ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
var vec = this.Mutable.ChatColours.TryGetValue(type, out var colour)
|
var vec = Mutable.ChatColours.TryGetValue(type, out var colour)
|
||||||
? ColourUtil.RgbaToVector3(colour)
|
? ColourUtil.RgbaToVector3(colour)
|
||||||
: ColourUtil.RgbaToVector3(type.DefaultColour() ?? 0);
|
: ColourUtil.RgbaToVector3(type.DefaultColour() ?? 0);
|
||||||
if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs)) {
|
if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs)) {
|
||||||
this.Mutable.ChatColours[type] = ColourUtil.Vector3ToRgba(vec);
|
Mutable.ChatColours[type] = ColourUtil.Vector3ToRgba(vec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,36 +11,36 @@ internal sealed class Database : ISettingsTab {
|
|||||||
public string Name => Language.Options_Database_Tab + "###tabs-database";
|
public string Name => Language.Options_Database_Tab + "###tabs-database";
|
||||||
|
|
||||||
internal Database(Configuration mutable, Store store) {
|
internal Database(Configuration mutable, Store store) {
|
||||||
this.Store = store;
|
Store = store;
|
||||||
this.Mutable = mutable;
|
Mutable = mutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _showAdvanced;
|
private bool _showAdvanced;
|
||||||
|
|
||||||
public void Draw(bool changed) {
|
public void Draw(bool changed) {
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this._showAdvanced = ImGui.GetIO().KeyShift;
|
_showAdvanced = ImGui.GetIO().KeyShift;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(ref this.Mutable.DatabaseBattleMessages, Language.Options_DatabaseBattleMessages_Name, Language.Options_DatabaseBattleMessages_Description);
|
ImGuiUtil.OptionCheckbox(ref Mutable.DatabaseBattleMessages, Language.Options_DatabaseBattleMessages_Name, Language.Options_DatabaseBattleMessages_Description);
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
if (ImGuiUtil.OptionCheckbox(ref this.Mutable.LoadPreviousSession, Language.Options_LoadPreviousSession_Name, Language.Options_LoadPreviousSession_Description)) {
|
if (ImGuiUtil.OptionCheckbox(ref Mutable.LoadPreviousSession, Language.Options_LoadPreviousSession_Name, Language.Options_LoadPreviousSession_Description)) {
|
||||||
if (this.Mutable.LoadPreviousSession) {
|
if (Mutable.LoadPreviousSession) {
|
||||||
this.Mutable.FilterIncludePreviousSessions = true;
|
Mutable.FilterIncludePreviousSessions = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
if (ImGuiUtil.OptionCheckbox(ref this.Mutable.FilterIncludePreviousSessions, Language.Options_FilterIncludePreviousSessions_Name, Language.Options_FilterIncludePreviousSessions_Description)) {
|
if (ImGuiUtil.OptionCheckbox(ref Mutable.FilterIncludePreviousSessions, Language.Options_FilterIncludePreviousSessions_Name, Language.Options_FilterIncludePreviousSessions_Description)) {
|
||||||
if (!this.Mutable.FilterIncludePreviousSessions) {
|
if (!Mutable.FilterIncludePreviousSessions) {
|
||||||
this.Mutable.LoadPreviousSession = false;
|
Mutable.LoadPreviousSession = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(
|
ImGuiUtil.OptionCheckbox(
|
||||||
ref this.Mutable.SharedMode,
|
ref Mutable.SharedMode,
|
||||||
Language.Options_SharedMode_Name,
|
Language.Options_SharedMode_Name,
|
||||||
string.Format(Language.Options_SharedMode_Description, Plugin.PluginName)
|
string.Format(Language.Options_SharedMode_Description, Plugin.PluginName)
|
||||||
);
|
);
|
||||||
@@ -48,16 +48,16 @@ internal sealed class Database : ISettingsTab {
|
|||||||
|
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
if (this._showAdvanced && ImGui.TreeNodeEx(Language.Options_Database_Advanced)) {
|
if (_showAdvanced && ImGui.TreeNodeEx(Language.Options_Database_Advanced)) {
|
||||||
ImGui.PushTextWrapPos();
|
ImGui.PushTextWrapPos();
|
||||||
ImGuiUtil.WarningText(Language.Options_Database_Advanced_Warning);
|
ImGuiUtil.WarningText(Language.Options_Database_Advanced_Warning);
|
||||||
|
|
||||||
if (ImGui.Button("Checkpoint")) {
|
if (ImGui.Button("Checkpoint")) {
|
||||||
this.Store.Database.Checkpoint();
|
Store.Database.Checkpoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.Button("Rebuild")) {
|
if (ImGui.Button("Rebuild")) {
|
||||||
this.Store.Database.Rebuild();
|
Store.Database.Rebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PopTextWrapPos();
|
ImGui.PopTextWrapPos();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using ChatTwo.Resources;
|
using ChatTwo.Resources;
|
||||||
using ChatTwo.Util;
|
using ChatTwo.Util;
|
||||||
|
using Dalamud.Interface.Style;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace ChatTwo.Ui.SettingsTabs;
|
||||||
@@ -40,6 +41,12 @@ internal sealed class Display : ISettingsTab {
|
|||||||
);
|
);
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
ImGuiUtil.OptionCheckbox(
|
||||||
|
ref Mutable.HideInLoadingScreens,
|
||||||
|
Language.Options_HideInLoadingScreens_Name,
|
||||||
|
string.Format(Language.Options_HideInLoadingScreens_Description, Plugin.PluginName));
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(
|
ImGuiUtil.OptionCheckbox(
|
||||||
ref Mutable.NativeItemTooltips,
|
ref Mutable.NativeItemTooltips,
|
||||||
Language.Options_NativeItemTooltips_Name,
|
Language.Options_NativeItemTooltips_Name,
|
||||||
@@ -89,6 +96,24 @@ internal sealed class Display : ISettingsTab {
|
|||||||
ImGuiUtil.OptionCheckbox(ref Mutable.ShowPopOutTitleBar, Language.Options_ShowPopOutTitleBar_Name);
|
ImGuiUtil.OptionCheckbox(ref Mutable.ShowPopOutTitleBar, Language.Options_ShowPopOutTitleBar_Name);
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
ImGuiUtil.OptionCheckbox(ref Mutable.OverrideStyle, Language.Options_OverrideStyle_Name, Language.Options_OverrideStyle_Name_Desc);
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
if (Mutable.OverrideStyle)
|
||||||
|
{
|
||||||
|
var currentStyle = Mutable.ChosenStyle.Equals("") ? StyleModel.GetConfiguredStyle().Name : Mutable.ChosenStyle;
|
||||||
|
if (ImGui.BeginCombo(Language.Options_OverrideStyleDropdown_Name, currentStyle)) {
|
||||||
|
foreach (var style in StyleModel.GetConfiguredStyles()) {
|
||||||
|
if (ImGui.Selectable(style.Name, Mutable.ChosenStyle == style.Name)) {
|
||||||
|
Mutable.ChosenStyle = style.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndCombo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
ImGui.PopTextWrapPos();
|
ImGui.PopTextWrapPos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,45 +12,45 @@ public class Fonts : ISettingsTab {
|
|||||||
private List<string> JpFonts { get; set; } = new();
|
private List<string> JpFonts { get; set; } = new();
|
||||||
|
|
||||||
internal Fonts(Configuration mutable) {
|
internal Fonts(Configuration mutable) {
|
||||||
this.Mutable = mutable;
|
Mutable = mutable;
|
||||||
this.UpdateFonts();
|
UpdateFonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateFonts() {
|
private void UpdateFonts() {
|
||||||
this.GlobalFonts = Ui.Fonts.GetFonts();
|
GlobalFonts = Ui.Fonts.GetFonts();
|
||||||
this.JpFonts = Ui.Fonts.GetJpFonts();
|
JpFonts = Ui.Fonts.GetJpFonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(bool changed) {
|
public void Draw(bool changed) {
|
||||||
if (changed) {
|
if (changed) {
|
||||||
this.UpdateFonts();
|
UpdateFonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PushTextWrapPos();
|
ImGui.PushTextWrapPos();
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_FontsEnabled, ref this.Mutable.FontsEnabled);
|
ImGui.Checkbox(Language.Options_FontsEnabled, ref Mutable.FontsEnabled);
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
if (this.Mutable.FontsEnabled) {
|
if (Mutable.FontsEnabled) {
|
||||||
if (ImGuiUtil.BeginComboVertical(Language.Options_Font_Name, this.Mutable.GlobalFont)) {
|
if (ImGuiUtil.BeginComboVertical(Language.Options_Font_Name, Mutable.GlobalFont)) {
|
||||||
foreach (var font in Ui.Fonts.GlobalFonts) {
|
foreach (var font in Ui.Fonts.GlobalFonts) {
|
||||||
if (ImGui.Selectable(font.Name, this.Mutable.GlobalFont == font.Name)) {
|
if (ImGui.Selectable(font.Name, Mutable.GlobalFont == font.Name)) {
|
||||||
this.Mutable.GlobalFont = font.Name;
|
Mutable.GlobalFont = font.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsWindowAppearing() && this.Mutable.GlobalFont == font.Name) {
|
if (ImGui.IsWindowAppearing() && Mutable.GlobalFont == font.Name) {
|
||||||
ImGui.SetScrollHereY(0.5f);
|
ImGui.SetScrollHereY(0.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
foreach (var name in this.GlobalFonts) {
|
foreach (var name in GlobalFonts) {
|
||||||
if (ImGui.Selectable(name, this.Mutable.GlobalFont == name)) {
|
if (ImGui.Selectable(name, Mutable.GlobalFont == name)) {
|
||||||
this.Mutable.GlobalFont = name;
|
Mutable.GlobalFont = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsWindowAppearing() && this.Mutable.GlobalFont == name) {
|
if (ImGui.IsWindowAppearing() && Mutable.GlobalFont == name) {
|
||||||
ImGui.SetScrollHereY(0.5f);
|
ImGui.SetScrollHereY(0.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,25 +62,25 @@ public class Fonts : ISettingsTab {
|
|||||||
ImGuiUtil.WarningText(Language.Options_Font_Warning);
|
ImGuiUtil.WarningText(Language.Options_Font_Warning);
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
if (ImGuiUtil.BeginComboVertical(Language.Options_JapaneseFont_Name, this.Mutable.JapaneseFont)) {
|
if (ImGuiUtil.BeginComboVertical(Language.Options_JapaneseFont_Name, Mutable.JapaneseFont)) {
|
||||||
foreach (var (name, _) in Ui.Fonts.JapaneseFonts) {
|
foreach (var (name, _) in Ui.Fonts.JapaneseFonts) {
|
||||||
if (ImGui.Selectable(name, this.Mutable.JapaneseFont == name)) {
|
if (ImGui.Selectable(name, Mutable.JapaneseFont == name)) {
|
||||||
this.Mutable.JapaneseFont = name;
|
Mutable.JapaneseFont = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsWindowAppearing() && this.Mutable.JapaneseFont == name) {
|
if (ImGui.IsWindowAppearing() && Mutable.JapaneseFont == name) {
|
||||||
ImGui.SetScrollHereY(0.5f);
|
ImGui.SetScrollHereY(0.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
foreach (var family in this.JpFonts) {
|
foreach (var family in JpFonts) {
|
||||||
if (ImGui.Selectable(family, this.Mutable.JapaneseFont == family)) {
|
if (ImGui.Selectable(family, Mutable.JapaneseFont == family)) {
|
||||||
this.Mutable.JapaneseFont = family;
|
Mutable.JapaneseFont = family;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsWindowAppearing() && this.Mutable.JapaneseFont == family) {
|
if (ImGui.IsWindowAppearing() && Mutable.JapaneseFont == family) {
|
||||||
ImGui.SetScrollHereY(0.5f);
|
ImGui.SetScrollHereY(0.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,12 +94,12 @@ public class Fonts : ISettingsTab {
|
|||||||
if (ImGui.CollapsingHeader(Language.Options_ExtraGlyphs_Name)) {
|
if (ImGui.CollapsingHeader(Language.Options_ExtraGlyphs_Name)) {
|
||||||
ImGuiUtil.HelpText(string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName));
|
ImGuiUtil.HelpText(string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName));
|
||||||
|
|
||||||
var range = (int) this.Mutable.ExtraGlyphRanges;
|
var range = (int) Mutable.ExtraGlyphRanges;
|
||||||
foreach (var extra in Enum.GetValues<ExtraGlyphRanges>()) {
|
foreach (var extra in Enum.GetValues<ExtraGlyphRanges>()) {
|
||||||
ImGui.CheckboxFlags(extra.Name(), ref range, (int) extra);
|
ImGui.CheckboxFlags(extra.Name(), ref range, (int) extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Mutable.ExtraGlyphRanges = (ExtraGlyphRanges) range;
|
Mutable.ExtraGlyphRanges = (ExtraGlyphRanges) range;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
@@ -108,9 +108,9 @@ public class Fonts : ISettingsTab {
|
|||||||
const float speed = .0125f;
|
const float speed = .0125f;
|
||||||
const float min = 8f;
|
const float min = 8f;
|
||||||
const float max = 36f;
|
const float max = 36f;
|
||||||
ImGuiUtil.DragFloatVertical(Language.Options_FontSize_Name, ref this.Mutable.FontSize, speed, min, max, $"{this.Mutable.FontSize:N1}");
|
ImGuiUtil.DragFloatVertical(Language.Options_FontSize_Name, ref Mutable.FontSize, speed, min, max, $"{Mutable.FontSize:N1}");
|
||||||
ImGuiUtil.DragFloatVertical(Language.Options_JapaneseFontSize_Name, ref this.Mutable.JapaneseFontSize, speed, min, max, $"{this.Mutable.JapaneseFontSize:N1}");
|
ImGuiUtil.DragFloatVertical(Language.Options_JapaneseFontSize_Name, ref Mutable.JapaneseFontSize, speed, min, max, $"{Mutable.JapaneseFontSize:N1}");
|
||||||
ImGuiUtil.DragFloatVertical(Language.Options_SymbolsFontSize_Name, ref this.Mutable.SymbolsFontSize, speed, min, max, $"{this.Mutable.SymbolsFontSize:N1}");
|
ImGuiUtil.DragFloatVertical(Language.Options_SymbolsFontSize_Name, ref Mutable.SymbolsFontSize, speed, min, max, $"{Mutable.SymbolsFontSize:N1}");
|
||||||
ImGuiUtil.HelpText(Language.Options_SymbolsFontSize_Description);
|
ImGuiUtil.HelpText(Language.Options_SymbolsFontSize_Description);
|
||||||
|
|
||||||
ImGui.PopTextWrapPos();
|
ImGui.PopTextWrapPos();
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ internal sealed class Miscellaneous : ISettingsTab {
|
|||||||
public string Name => Language.Options_Miscellaneous_Tab + "###tabs-miscellaneous";
|
public string Name => Language.Options_Miscellaneous_Tab + "###tabs-miscellaneous";
|
||||||
|
|
||||||
public Miscellaneous(Configuration mutable) {
|
public Miscellaneous(Configuration mutable) {
|
||||||
this.Mutable = mutable;
|
Mutable = mutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(bool changed) {
|
public void Draw(bool changed) {
|
||||||
if (ImGuiUtil.BeginComboVertical(Language.Options_Language_Name, this.Mutable.LanguageOverride.Name())) {
|
if (ImGuiUtil.BeginComboVertical(Language.Options_Language_Name, Mutable.LanguageOverride.Name())) {
|
||||||
foreach (var language in Enum.GetValues<LanguageOverride>()) {
|
foreach (var language in Enum.GetValues<LanguageOverride>()) {
|
||||||
if (ImGui.Selectable(language.Name())) {
|
if (ImGui.Selectable(language.Name())) {
|
||||||
this.Mutable.LanguageOverride = language;
|
Mutable.LanguageOverride = language;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,10 +27,10 @@ internal sealed class Miscellaneous : ISettingsTab {
|
|||||||
ImGuiUtil.HelpText(string.Format(Language.Options_Language_Description, Plugin.PluginName));
|
ImGuiUtil.HelpText(string.Format(Language.Options_Language_Description, Plugin.PluginName));
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
if (ImGuiUtil.BeginComboVertical(Language.Options_CommandHelpSide_Name, this.Mutable.CommandHelpSide.Name())) {
|
if (ImGuiUtil.BeginComboVertical(Language.Options_CommandHelpSide_Name, Mutable.CommandHelpSide.Name())) {
|
||||||
foreach (var side in Enum.GetValues<CommandHelpSide>()) {
|
foreach (var side in Enum.GetValues<CommandHelpSide>()) {
|
||||||
if (ImGui.Selectable(side.Name(), this.Mutable.CommandHelpSide == side)) {
|
if (ImGui.Selectable(side.Name(), Mutable.CommandHelpSide == side)) {
|
||||||
this.Mutable.CommandHelpSide = side;
|
Mutable.CommandHelpSide = side;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,10 +40,10 @@ internal sealed class Miscellaneous : ISettingsTab {
|
|||||||
ImGuiUtil.HelpText(string.Format(Language.Options_CommandHelpSide_Description, Plugin.PluginName));
|
ImGuiUtil.HelpText(string.Format(Language.Options_CommandHelpSide_Description, Plugin.PluginName));
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
if (ImGuiUtil.BeginComboVertical(Language.Options_KeybindMode_Name, this.Mutable.KeybindMode.Name())) {
|
if (ImGuiUtil.BeginComboVertical(Language.Options_KeybindMode_Name, Mutable.KeybindMode.Name())) {
|
||||||
foreach (var mode in Enum.GetValues<KeybindMode>()) {
|
foreach (var mode in Enum.GetValues<KeybindMode>()) {
|
||||||
if (ImGui.Selectable(mode.Name(), this.Mutable.KeybindMode == mode)) {
|
if (ImGui.Selectable(mode.Name(), Mutable.KeybindMode == mode)) {
|
||||||
this.Mutable.KeybindMode = mode;
|
Mutable.KeybindMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemHovered()) {
|
if (ImGui.IsItemHovered()) {
|
||||||
@@ -59,7 +59,7 @@ internal sealed class Miscellaneous : ISettingsTab {
|
|||||||
ImGuiUtil.HelpText(string.Format(Language.Options_KeybindMode_Description, Plugin.PluginName));
|
ImGuiUtil.HelpText(string.Format(Language.Options_KeybindMode_Description, Plugin.PluginName));
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_SortAutoTranslate_Name, ref this.Mutable.SortAutoTranslate);
|
ImGui.Checkbox(Language.Options_SortAutoTranslate_Name, ref Mutable.SortAutoTranslate);
|
||||||
ImGuiUtil.HelpText(Language.Options_SortAutoTranslate_Description);
|
ImGuiUtil.HelpText(Language.Options_SortAutoTranslate_Description);
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ internal sealed class Tabs : ISettingsTab {
|
|||||||
private int _toOpen = -2;
|
private int _toOpen = -2;
|
||||||
|
|
||||||
internal Tabs(Plugin plugin, Configuration mutable) {
|
internal Tabs(Plugin plugin, Configuration mutable) {
|
||||||
this.Plugin = plugin;
|
Plugin = plugin;
|
||||||
this.Mutable = mutable;
|
Mutable = mutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(bool changed) {
|
public void Draw(bool changed) {
|
||||||
@@ -28,29 +28,29 @@ internal sealed class Tabs : ISettingsTab {
|
|||||||
|
|
||||||
if (ImGui.BeginPopup(addTabPopup)) {
|
if (ImGui.BeginPopup(addTabPopup)) {
|
||||||
if (ImGui.Selectable(Language.Options_Tabs_NewTab)) {
|
if (ImGui.Selectable(Language.Options_Tabs_NewTab)) {
|
||||||
this.Mutable.Tabs.Add(new Tab());
|
Mutable.Tabs.Add(new Tab());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
if (ImGui.Selectable(string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_General))) {
|
if (ImGui.Selectable(string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_General))) {
|
||||||
this.Mutable.Tabs.Add(TabsUtil.VanillaGeneral);
|
Mutable.Tabs.Add(TabsUtil.VanillaGeneral);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.Selectable(string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_Event))) {
|
if (ImGui.Selectable(string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_Event))) {
|
||||||
this.Mutable.Tabs.Add(TabsUtil.VanillaEvent);
|
Mutable.Tabs.Add(TabsUtil.VanillaEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndPopup();
|
ImGui.EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toRemove = -1;
|
var toRemove = -1;
|
||||||
var doOpens = this._toOpen > -2;
|
var doOpens = _toOpen > -2;
|
||||||
for (var i = 0; i < this.Mutable.Tabs.Count; i++) {
|
for (var i = 0; i < Mutable.Tabs.Count; i++) {
|
||||||
var tab = this.Mutable.Tabs[i];
|
var tab = Mutable.Tabs[i];
|
||||||
|
|
||||||
if (doOpens) {
|
if (doOpens) {
|
||||||
ImGui.SetNextItemOpen(i == this._toOpen);
|
ImGui.SetNextItemOpen(i == _toOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.TreeNodeEx($"{tab.Name}###tab-{i}")) {
|
if (ImGui.TreeNodeEx($"{tab.Name}###tab-{i}")) {
|
||||||
@@ -58,21 +58,21 @@ internal sealed class Tabs : ISettingsTab {
|
|||||||
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.TrashAlt, tooltip: Language.Options_Tabs_Delete)) {
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.TrashAlt, tooltip: Language.Options_Tabs_Delete)) {
|
||||||
toRemove = i;
|
toRemove = i;
|
||||||
this._toOpen = -1;
|
_toOpen = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.ArrowUp, tooltip: Language.Options_Tabs_MoveUp) && i > 0) {
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.ArrowUp, tooltip: Language.Options_Tabs_MoveUp) && i > 0) {
|
||||||
(this.Mutable.Tabs[i - 1], this.Mutable.Tabs[i]) = (this.Mutable.Tabs[i], this.Mutable.Tabs[i - 1]);
|
(Mutable.Tabs[i - 1], Mutable.Tabs[i]) = (Mutable.Tabs[i], Mutable.Tabs[i - 1]);
|
||||||
this._toOpen = i - 1;
|
_toOpen = i - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.ArrowDown, tooltip: Language.Options_Tabs_MoveDown) && i < this.Mutable.Tabs.Count - 1) {
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.ArrowDown, tooltip: Language.Options_Tabs_MoveDown) && i < Mutable.Tabs.Count - 1) {
|
||||||
(this.Mutable.Tabs[i + 1], this.Mutable.Tabs[i]) = (this.Mutable.Tabs[i], this.Mutable.Tabs[i + 1]);
|
(Mutable.Tabs[i + 1], Mutable.Tabs[i]) = (Mutable.Tabs[i], Mutable.Tabs[i + 1]);
|
||||||
this._toOpen = i + 1;
|
_toOpen = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.InputText(Language.Options_Tabs_Name, ref tab.Name, 512, ImGuiInputTextFlags.EnterReturnsTrue);
|
ImGui.InputText(Language.Options_Tabs_Name, ref tab.Name, 512, ImGuiInputTextFlags.EnterReturnsTrue);
|
||||||
@@ -160,7 +160,7 @@ internal sealed class Tabs : ISettingsTab {
|
|||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.Plugin.ExtraChat.ChannelNames.Count > 0 && ImGui.TreeNodeEx(Language.Options_Tabs_ExtraChatChannels)) {
|
if (Plugin.ExtraChat.ChannelNames.Count > 0 && ImGui.TreeNodeEx(Language.Options_Tabs_ExtraChatChannels)) {
|
||||||
ImGui.Checkbox(Language.Options_Tabs_ExtraChatAll, ref tab.ExtraChatAll);
|
ImGui.Checkbox(Language.Options_Tabs_ExtraChatAll, ref tab.ExtraChatAll);
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
@@ -169,7 +169,7 @@ internal sealed class Tabs : ISettingsTab {
|
|||||||
ImGui.BeginDisabled();
|
ImGui.BeginDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (id, name) in this.Plugin.ExtraChat.ChannelNames) {
|
foreach (var (id, name) in Plugin.ExtraChat.ChannelNames) {
|
||||||
var enabled = tab.ExtraChatChannels.Contains(id);
|
var enabled = tab.ExtraChatChannels.Contains(id);
|
||||||
if (!ImGui.Checkbox($"{name}##ec-{id}", ref enabled)) {
|
if (!ImGui.Checkbox($"{name}##ec-{id}", ref enabled)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -196,11 +196,11 @@ internal sealed class Tabs : ISettingsTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toRemove > -1) {
|
if (toRemove > -1) {
|
||||||
this.Mutable.Tabs.RemoveAt(toRemove);
|
Mutable.Tabs.RemoveAt(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doOpens) {
|
if (doOpens) {
|
||||||
this._toOpen = -2;
|
_toOpen = -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ internal class SingleRow : ISelectorPart {
|
|||||||
public uint Row { get; }
|
public uint Row { get; }
|
||||||
|
|
||||||
public SingleRow(uint row) {
|
public SingleRow(uint row) {
|
||||||
this.Row = row;
|
Row = row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,8 +287,8 @@ internal class IndexRange : ISelectorPart {
|
|||||||
public uint End { get; }
|
public uint End { get; }
|
||||||
|
|
||||||
public IndexRange(uint start, uint end) {
|
public IndexRange(uint start, uint end) {
|
||||||
this.Start = start;
|
Start = start;
|
||||||
this.End = end;
|
End = end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +299,7 @@ internal class ColumnSpecifier : ISelectorPart {
|
|||||||
public uint Column { get; }
|
public uint Column { get; }
|
||||||
|
|
||||||
public ColumnSpecifier(uint column) {
|
public ColumnSpecifier(uint column) {
|
||||||
this.Column = column;
|
Column = column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,9 +310,9 @@ internal class AutoTranslateEntry {
|
|||||||
internal SeString SeString { get; }
|
internal SeString SeString { get; }
|
||||||
|
|
||||||
public AutoTranslateEntry(uint group, uint row, string str, SeString seStr) {
|
public AutoTranslateEntry(uint group, uint row, string str, SeString seStr) {
|
||||||
this.Group = group;
|
Group = group;
|
||||||
this.Row = row;
|
Row = row;
|
||||||
this.String = str;
|
String = str;
|
||||||
this.SeString = seStr;
|
SeString = seStr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using ChatTwo.Code;
|
using ChatTwo.Code;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace ChatTwo.Util;
|
namespace ChatTwo.Util;
|
||||||
|
|
||||||
@@ -68,15 +69,19 @@ internal static class ChunkUtil {
|
|||||||
break;
|
break;
|
||||||
case PayloadType.Unknown:
|
case PayloadType.Unknown:
|
||||||
var rawPayload = (RawPayload) payload;
|
var rawPayload = (RawPayload) payload;
|
||||||
if (rawPayload.Data.Length > 1 && rawPayload.Data[1] == 0x13)
|
var colorPayload = ColorPayload.From(rawPayload.Data);
|
||||||
|
if (colorPayload != null)
|
||||||
{
|
{
|
||||||
if (foreground.Count > 0) {
|
if (colorPayload.Enabled)
|
||||||
foreground.Pop();
|
|
||||||
}
|
|
||||||
else if (rawPayload.Data.Length > 6 && rawPayload.Data[2] == 0x05 && rawPayload.Data[3] == 0xF6)
|
|
||||||
{
|
{
|
||||||
var (r, g, b) = (rawPayload.Data[4], rawPayload.Data[5], rawPayload.Data[6]);
|
if (colorPayload.Color > 0)
|
||||||
foreground.Push(ColourUtil.ComponentsToRgba(r, g, b));
|
foreground.Push(colorPayload.Color);
|
||||||
|
else if (foreground.Count > 0) // Push the previous color as we don't want invisible text
|
||||||
|
foreground.Push(foreground.Peek());
|
||||||
|
}
|
||||||
|
else if (foreground.Count > 0)
|
||||||
|
{
|
||||||
|
foreground.Pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (rawPayload.Data.Length > 1 && rawPayload.Data[1] == 0x14)
|
else if (rawPayload.Data.Length > 1 && rawPayload.Data[1] == 0x14)
|
||||||
@@ -100,6 +105,10 @@ internal static class ChunkUtil {
|
|||||||
var reader = new BinaryReader(new MemoryStream(rawPayload.Data[4..]));
|
var reader = new BinaryReader(new MemoryStream(rawPayload.Data[4..]));
|
||||||
var id = GetInteger(reader);
|
var id = GetInteger(reader);
|
||||||
link = new AchievementPayload(id);
|
link = new AchievementPayload(id);
|
||||||
|
} else if (rawPayload.Data.Length > 5 && rawPayload.Data[1] == 0x27 && rawPayload.Data[3] == 0x07) {
|
||||||
|
// uri payload
|
||||||
|
var uri = new Uri(Encoding.UTF8.GetString(rawPayload.Data[4..]));
|
||||||
|
link = new URIPayload(uri);
|
||||||
} else if (Equals(rawPayload, RawPayload.LinkTerminator)) {
|
} else if (Equals(rawPayload, RawPayload.LinkTerminator)) {
|
||||||
link = null;
|
link = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ internal static class ColourUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal static uint RgbaToAbgr(uint rgba) {
|
internal static uint RgbaToAbgr(uint rgba) {
|
||||||
var (r, g, b, a) = RgbaToComponents(rgba);
|
var tmp = ((rgba << 8) & 0xFF00FF00) | ((rgba >> 8) & 0xFF00FF);
|
||||||
return (uint) ((a << 24) | (b << 16) | (g << 8) | r);
|
return (tmp << 16) | (tmp >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Vector3 RgbaToVector3(uint rgba) {
|
internal static Vector3 RgbaToVector3(uint rgba) {
|
||||||
@@ -38,6 +38,13 @@ internal static class ColourUtil {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static unsafe uint ArgbToRgba(uint x)
|
||||||
|
{
|
||||||
|
var buf = (byte*)&x;
|
||||||
|
(buf[1], buf[2], buf[3], buf[0]) = (buf[0], buf[1], buf[2], buf[3]);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
internal static uint ComponentsToRgba(byte red, byte green, byte blue, byte alpha = 0xFF) => alpha
|
internal static uint ComponentsToRgba(byte red, byte green, byte blue, byte alpha = 0xFF) => alpha
|
||||||
| (uint) (red << 24)
|
| (uint) (red << 24)
|
||||||
| (uint) (green << 16)
|
| (uint) (green << 16)
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using Dalamud.Utility;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.Text;
|
||||||
|
|
||||||
|
namespace ChatTwo.Util;
|
||||||
|
|
||||||
|
public class ColorPayload
|
||||||
|
{
|
||||||
|
private const byte START_BYTE = 2;
|
||||||
|
|
||||||
|
public bool Enabled;
|
||||||
|
public uint Color;
|
||||||
|
public uint UnshiftedColor;
|
||||||
|
|
||||||
|
public static ColorPayload? From(byte[] data)
|
||||||
|
{
|
||||||
|
using var stream = new MemoryStream(data);
|
||||||
|
if (stream.ReadByte() != START_BYTE || stream.ReadByte() != 0x13)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
stream.ReadByte(); // skip the length byte;
|
||||||
|
|
||||||
|
var typeByte = stream.ReadByte();
|
||||||
|
var payload = new ColorPayload();
|
||||||
|
if (typeByte == 0xEC)
|
||||||
|
{
|
||||||
|
payload.Enabled = false;
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeByte == 0xE9)
|
||||||
|
{
|
||||||
|
var param = stream.ReadByte();
|
||||||
|
var ok = TryGetGNumDefault((uint) (param - 2), out var value);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
Plugin.Log.Error($"Unable to GetGNum for param {param - 2}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.Enabled = true;
|
||||||
|
payload.UnshiftedColor = value;
|
||||||
|
payload.Color = ColourUtil.ArgbToRgba(value);
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeByte is >= 0xF0 and <= 0xFE)
|
||||||
|
{
|
||||||
|
// From: https://github.com/NotAdam/Lumina/blob/master/src/Lumina/Text/Expressions/IntegerExpression.cs#L119-L128
|
||||||
|
uint ShiftAndThrowIfZero(int v, int shift)
|
||||||
|
{
|
||||||
|
return v switch
|
||||||
|
{
|
||||||
|
-1 => throw new ArgumentException("Encountered premature end of input (unexpected EOF).", nameof(v)),
|
||||||
|
0 => throw new ArgumentException("Encountered premature end of input (unexpected null character).", nameof(v)),
|
||||||
|
_ => (uint)v << shift
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
typeByte += 1;
|
||||||
|
var value = 0u;
|
||||||
|
if ((typeByte & 8) != 0)
|
||||||
|
value |= ShiftAndThrowIfZero(stream.ReadByte(), 24);
|
||||||
|
else
|
||||||
|
value |= 0xff000000u;
|
||||||
|
|
||||||
|
if( (typeByte & 4) != 0 ) value |= ShiftAndThrowIfZero( stream.ReadByte(), 16 );
|
||||||
|
if( (typeByte & 2) != 0 ) value |= ShiftAndThrowIfZero( stream.ReadByte(), 8 );
|
||||||
|
if( (typeByte & 1) != 0 ) value |= ShiftAndThrowIfZero( stream.ReadByte(), 0 );
|
||||||
|
|
||||||
|
payload.Enabled = true;
|
||||||
|
payload.Color = ColourUtil.ArgbToRgba(value);
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe bool TryGetGNumDefault(uint parameterIndex, out uint value)
|
||||||
|
{
|
||||||
|
value = 0u;
|
||||||
|
|
||||||
|
var rtm = RaptureTextModule.Instance();
|
||||||
|
if (rtm is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ThreadSafety.IsMainThread)
|
||||||
|
{
|
||||||
|
Plugin.Log.Error("Global parameters may only be used from the main thread.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var gp = ref rtm->TextModule.MacroDecoder.GlobalParameters;
|
||||||
|
if (parameterIndex >= gp.MySize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var p = rtm->TextModule.MacroDecoder.GlobalParameters.Get(parameterIndex);
|
||||||
|
switch (p.Type)
|
||||||
|
{
|
||||||
|
case TextParameterType.Integer:
|
||||||
|
value = (uint)p.IntValue;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+144
-91
@@ -1,95 +1,148 @@
|
|||||||
using System.Numerics;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ChatTwo.Util;
|
namespace ChatTwo.Util;
|
||||||
|
|
||||||
internal static class IconUtil {
|
// From Kizer: https://github.com/Soreepeong/Dalamud/blob/feature/log-wordwrap/Dalamud/Interface/Spannables/Internal/GfdFileView.cs
|
||||||
internal static Vector4? GetBounds(byte id) => id switch {
|
public readonly unsafe ref struct GfdFileView
|
||||||
1 => new Vector4(0, 342, 40, 40),
|
{
|
||||||
2 => new Vector4(40, 342, 40, 40),
|
private readonly ReadOnlySpan<byte> Span;
|
||||||
3 => new Vector4(80, 342, 40, 40),
|
private readonly bool DirectLookup;
|
||||||
4 => new Vector4(120, 342, 40, 40),
|
|
||||||
5 => new Vector4(160, 342, 40, 40),
|
/// <summary>Initializes a new instance of the <see cref="GfdFileView"/> struct.</summary>
|
||||||
6 => new Vector4(0, 382, 40, 40),
|
/// <param name="span">The data.</param>
|
||||||
7 => new Vector4(40, 382, 40, 40),
|
public GfdFileView(ReadOnlySpan<byte> span)
|
||||||
8 => new Vector4(80, 382, 40, 40),
|
{
|
||||||
9 => new Vector4(120, 382, 40, 40),
|
Span = span;
|
||||||
10 => new Vector4(160, 382, 40, 40),
|
if (span.Length < sizeof(GfdHeader))
|
||||||
11 => new Vector4(0, 422, 40, 40),
|
throw new InvalidDataException($"Not enough space for a {nameof(GfdHeader)}");
|
||||||
12 => new Vector4(40, 422, 40, 40),
|
if (span.Length < sizeof(GfdHeader) + (Header.Count * sizeof(GfdEntry)))
|
||||||
13 => new Vector4(80, 422, 40, 40),
|
throw new InvalidDataException($"Not enough space for all the {nameof(GfdEntry)}");
|
||||||
14 => new Vector4(120, 422, 40, 40),
|
|
||||||
15 => new Vector4(160, 422, 40, 40),
|
var entries = Entries;
|
||||||
16 => new Vector4(120, 542, 40, 40),
|
DirectLookup = true;
|
||||||
17 => new Vector4(160, 542, 40, 40),
|
for (var i = 0; i < entries.Length && DirectLookup; i++)
|
||||||
18 => new Vector4(0, 462, 108, 40),
|
DirectLookup &= i + 1 == entries[i].Id;
|
||||||
19 => new Vector4(108, 462, 108, 40),
|
}
|
||||||
20 => new Vector4(120, 502, 40, 40),
|
|
||||||
21 => new Vector4(0, 502, 56, 40),
|
/// <summary>Gets the header.</summary>
|
||||||
22 => new Vector4(56, 502, 64, 40),
|
private ref readonly GfdHeader Header => ref MemoryMarshal.AsRef<GfdHeader>(Span);
|
||||||
23 => new Vector4(160, 502, 40, 40),
|
|
||||||
24 => new Vector4(0, 542, 56, 40),
|
/// <summary>Gets the entries.</summary>
|
||||||
25 => new Vector4(56, 542, 64, 40),
|
private ReadOnlySpan<GfdEntry> Entries => MemoryMarshal.Cast<byte, GfdEntry>(Span[sizeof(GfdHeader)..]);
|
||||||
51 => new Vector4(248, 342, 40, 40),
|
|
||||||
52 => new Vector4(288, 342, 40, 40),
|
/// <summary>Attempts to get an entry.</summary>
|
||||||
53 => new Vector4(328, 342, 40, 40),
|
/// <param name="iconId">The icon ID.</param>
|
||||||
54 => new Vector4(200, 342, 24, 40),
|
/// <param name="entry">The entry.</param>
|
||||||
55 => new Vector4(224, 342, 24, 40),
|
/// <param name="followRedirect">Whether to follow redirects.</param>
|
||||||
56 => new Vector4(200, 382, 40, 40),
|
/// <returns><c>true</c> if found.</returns>
|
||||||
57 => new Vector4(240, 382, 40, 40),
|
public bool TryGetEntry(uint iconId, out GfdEntry entry, bool followRedirect = true)
|
||||||
58 => new Vector4(280, 382, 40, 40),
|
{
|
||||||
59 => new Vector4(200, 422, 40, 40),
|
if (iconId == 0)
|
||||||
60 => new Vector4(240, 422, 40, 40),
|
{
|
||||||
61 => new Vector4(280, 422, 40, 40),
|
entry = default;
|
||||||
62 => new Vector4(320, 382, 40, 40),
|
return false;
|
||||||
63 => new Vector4(320, 422, 40, 40),
|
}
|
||||||
64 => new Vector4(368, 342, 40, 40),
|
|
||||||
65 => new Vector4(408, 342, 40, 40),
|
var entries = Entries;
|
||||||
66 => new Vector4(448, 342, 40, 40),
|
if (DirectLookup)
|
||||||
67 => new Vector4(360, 382, 40, 40),
|
{
|
||||||
68 => new Vector4(400, 382, 40, 40),
|
if (iconId <= entries.Length)
|
||||||
70 => new Vector4(360, 422, 40, 40),
|
{
|
||||||
71 => new Vector4(400, 422, 40, 40),
|
entry = entries[(int)(iconId - 1)];
|
||||||
72 => new Vector4(440, 422, 40, 40),
|
return !entry.IsEmpty;
|
||||||
73 => new Vector4(440, 382, 40, 40),
|
}
|
||||||
74 => new Vector4(216, 462, 40, 40),
|
|
||||||
75 => new Vector4(256, 462, 40, 40),
|
entry = default;
|
||||||
76 => new Vector4(296, 462, 40, 40),
|
return false;
|
||||||
77 => new Vector4(336, 462, 40, 40),
|
}
|
||||||
78 => new Vector4(376, 462, 40, 40),
|
|
||||||
79 => new Vector4(416, 462, 40, 40),
|
var lo = 0;
|
||||||
80 => new Vector4(456, 462, 40, 40),
|
var hi = entries.Length;
|
||||||
81 => new Vector4(200, 502, 40, 40),
|
while (lo <= hi)
|
||||||
82 => new Vector4(240, 502, 40, 40),
|
{
|
||||||
83 => new Vector4(280, 502, 40, 40),
|
var i = lo + ((hi - lo) >> 1);
|
||||||
84 => new Vector4(320, 502, 40, 40),
|
if (entries[i].Id == iconId)
|
||||||
85 => new Vector4(360, 502, 40, 40),
|
{
|
||||||
86 => new Vector4(400, 502, 40, 40),
|
if (followRedirect && entries[i].Redirect != 0)
|
||||||
87 => new Vector4(440, 502, 40, 40),
|
{
|
||||||
88 => new Vector4(200, 542, 40, 40),
|
iconId = entries[i].Redirect;
|
||||||
89 => new Vector4(240, 542, 40, 40),
|
lo = 0;
|
||||||
90 => new Vector4(280, 542, 40, 40),
|
hi = entries.Length;
|
||||||
91 => new Vector4(320, 542, 40, 40),
|
continue;
|
||||||
92 => new Vector4(360, 542, 40, 40),
|
}
|
||||||
93 => new Vector4(400, 542, 40, 40),
|
|
||||||
94 => new Vector4(440, 542, 40, 40),
|
entry = entries[i];
|
||||||
95 => new Vector4(0, 582, 40, 40),
|
return !entry.IsEmpty;
|
||||||
96 => new Vector4(40, 582, 40, 40),
|
}
|
||||||
97 => new Vector4(80, 582, 40, 40),
|
|
||||||
98 => new Vector4(120, 582, 40, 40),
|
if (entries[i].Id < iconId)
|
||||||
99 => new Vector4(160, 582, 40, 40),
|
lo = i + 1;
|
||||||
100 => new Vector4(200, 582, 40, 40),
|
else
|
||||||
101 => new Vector4(240, 582, 40, 40),
|
hi = i - 1;
|
||||||
102 => new Vector4(280, 582, 40, 40),
|
}
|
||||||
103 => new Vector4(320, 582, 40, 40),
|
|
||||||
104 => new Vector4(360, 582, 40, 40),
|
entry = default;
|
||||||
105 => new Vector4(400, 582, 40, 40),
|
return false;
|
||||||
106 => new Vector4(440, 582, 40, 40),
|
}
|
||||||
107 => new Vector4(0, 622, 40, 40),
|
|
||||||
108 => new Vector4(40, 622, 40, 40),
|
/// <summary>Header of a .gfd file.</summary>
|
||||||
109 => new Vector4(80, 622, 40, 40),
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
110 => new Vector4(120, 622, 40, 40),
|
public struct GfdHeader
|
||||||
111 => new Vector4(160, 622, 40, 40),
|
{
|
||||||
112 => new Vector4(200, 622, 40, 40),
|
/// <summary>Signature: "gftd0100".</summary>
|
||||||
_ => null,
|
public fixed byte Signature[8];
|
||||||
};
|
|
||||||
|
/// <summary>Number of entries.</summary>
|
||||||
|
public int Count;
|
||||||
|
|
||||||
|
/// <summary>Unused/unknown.</summary>
|
||||||
|
public fixed byte Padding[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>An entry of a .gfd file.</summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||||
|
public struct GfdEntry
|
||||||
|
{
|
||||||
|
/// <summary>ID of the entry.</summary>
|
||||||
|
public ushort Id;
|
||||||
|
|
||||||
|
/// <summary>The left offset of the entry.</summary>
|
||||||
|
public ushort Left;
|
||||||
|
|
||||||
|
/// <summary>The top offset of the entry.</summary>
|
||||||
|
public ushort Top;
|
||||||
|
|
||||||
|
/// <summary>The width of the entry.</summary>
|
||||||
|
public ushort Width;
|
||||||
|
|
||||||
|
/// <summary>The height of the entry.</summary>
|
||||||
|
public ushort Height;
|
||||||
|
|
||||||
|
/// <summary>Unknown/unused.</summary>
|
||||||
|
public ushort Unk0A;
|
||||||
|
|
||||||
|
/// <summary>The redirected entry, maybe.</summary>
|
||||||
|
public ushort Redirect;
|
||||||
|
|
||||||
|
/// <summary>Unknown/unused.</summary>
|
||||||
|
public ushort Unk0E;
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether this entry is effectively empty.</summary>
|
||||||
|
public bool IsEmpty => Width == 0 || Height == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal static class IconUtil {
|
||||||
|
private static byte[]? GfdFile;
|
||||||
|
public static unsafe GfdFileView GfdFileView
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
GfdFile ??= Plugin.DataManager.GetFile("common/font/gfdata.gfd")!.Data;
|
||||||
|
return new GfdFileView(new ReadOnlySpan<byte>(Unsafe.AsPointer(ref GfdFile[0]), GfdFile.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,16 +197,16 @@ internal static class ImGuiUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void WarningText(string text) {
|
internal static void WarningText(string text, bool wrap = true) {
|
||||||
var style = StyleModel.GetConfiguredStyle() ?? StyleModel.GetFromCurrent();
|
var style = StyleModel.GetConfiguredStyle() ?? StyleModel.GetFromCurrent();
|
||||||
var dalamudOrange = style.BuiltInColors?.DalamudOrange;
|
var dalamudOrange = style.BuiltInColors?.DalamudOrange;
|
||||||
if (dalamudOrange != null) {
|
if (dalamudOrange != null) {
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, dalamudOrange.Value);
|
ImGui.PushStyleColor(ImGuiCol.Text, dalamudOrange.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PushTextWrapPos();
|
if (wrap) ImGui.PushTextWrapPos();
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.PopTextWrapPos();
|
if (wrap) ImGui.PopTextWrapPos();
|
||||||
|
|
||||||
if (dalamudOrange != null) {
|
if (dalamudOrange != null) {
|
||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ internal class Lender<T> {
|
|||||||
private int _counter;
|
private int _counter;
|
||||||
|
|
||||||
internal Lender(Func<T> ctor) {
|
internal Lender(Func<T> ctor) {
|
||||||
this._ctor = ctor;
|
_ctor = ctor;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ResetCounter() {
|
internal void ResetCounter() {
|
||||||
this._counter = 0;
|
_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal T Borrow() {
|
internal T Borrow() {
|
||||||
if (this._items.Count <= this._counter) {
|
if (_items.Count <= _counter) {
|
||||||
this._items.Add(this._ctor());
|
_items.Add(_ctor());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._items[this._counter++];
|
return _items[_counter++];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ internal class PartyFinderPayload : Payload {
|
|||||||
internal uint Id { get; }
|
internal uint Id { get; }
|
||||||
|
|
||||||
internal PartyFinderPayload(uint id) {
|
internal PartyFinderPayload(uint id) {
|
||||||
this.Id = id;
|
Id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override byte[] EncodeImpl() {
|
protected override byte[] EncodeImpl() {
|
||||||
@@ -26,7 +26,7 @@ internal class AchievementPayload : Payload {
|
|||||||
internal uint Id { get; }
|
internal uint Id { get; }
|
||||||
|
|
||||||
internal AchievementPayload(uint id) {
|
internal AchievementPayload(uint id) {
|
||||||
this.Id = id;
|
Id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override byte[] EncodeImpl() {
|
protected override byte[] EncodeImpl() {
|
||||||
@@ -37,3 +37,51 @@ internal class AchievementPayload : Payload {
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class URIPayload(Uri uri) : Payload
|
||||||
|
{
|
||||||
|
public override PayloadType Type => (PayloadType) 0x52;
|
||||||
|
|
||||||
|
public Uri Uri { get; init; } = uri;
|
||||||
|
|
||||||
|
private static readonly string[] ExpectedSchemes = ["http", "https"];
|
||||||
|
private static readonly string DefaultScheme = "https";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a URIPayload from a raw URI string. If the URI does not have a
|
||||||
|
/// scheme, it will default to https://.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="UriFormatException">
|
||||||
|
/// If the URI is invalid, or if the scheme is not supported.
|
||||||
|
/// </exception>
|
||||||
|
public static URIPayload ResolveURI(string rawURI)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(rawURI);
|
||||||
|
|
||||||
|
// Check for expected scheme ://, if not add https://
|
||||||
|
foreach (var scheme in ExpectedSchemes)
|
||||||
|
{
|
||||||
|
if (rawURI.StartsWith($"{scheme}://"))
|
||||||
|
{
|
||||||
|
return new URIPayload(new Uri(rawURI));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rawURI.Contains("://"))
|
||||||
|
{
|
||||||
|
throw new UriFormatException($"Unsupported scheme in URL: {rawURI}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new URIPayload(new Uri($"{DefaultScheme}://{rawURI}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override byte[] EncodeImpl()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user