From de4ffd23c993aebb78912f12539b27342a243b77 Mon Sep 17 00:00:00 2001 From: NotNite Date: Sat, 18 Mar 2023 19:51:09 -0400 Subject: [PATCH 01/34] haplo-tier --- Dalamud/Fools/FoolsManager.cs | 77 +++++++++++++++++++ Dalamud/Fools/FoolsPluginMetadata.cs | 25 ++++++ Dalamud/Fools/IFoolsPlugin.cs | 8 ++ Dalamud/Fools/Plugins/TestFoolPlugin.cs | 16 ++++ .../Internal/PluginCategoryManager.cs | 31 +++++--- .../PluginInstaller/PluginInstallerWindow.cs | 41 ++++++++++ 6 files changed, 189 insertions(+), 9 deletions(-) create mode 100644 Dalamud/Fools/FoolsManager.cs create mode 100644 Dalamud/Fools/FoolsPluginMetadata.cs create mode 100644 Dalamud/Fools/IFoolsPlugin.cs create mode 100644 Dalamud/Fools/Plugins/TestFoolPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs new file mode 100644 index 000000000..8d08e5c39 --- /dev/null +++ b/Dalamud/Fools/FoolsManager.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Dalamud.Fools.Plugins; +using Dalamud.Logging.Internal; + +namespace Dalamud.Fools; + +/// +/// Manager for all the IFoolsPlugin instances. +/// +[ServiceManager.BlockingEarlyLoadedService] +internal class FoolsManager : IDisposable, IServiceType +{ + public readonly List FoolsPlugins = new(); + public readonly Dictionary ActivatedPlugins = new(); + + private static readonly ModuleLog Log = new("FOOLS"); + + [ServiceManager.ServiceConstructor] + private FoolsManager() + { + // reflect over all IFoolsPlugin implementations + this.FoolsPlugins = new List + { + new("Test Fool Plugin", "TestFoolPlugin", "this is a test", "NotNite", typeof(TestFoolPlugin)), + }; + } + + public void ActivatePlugin(string plugin) + { + if (this.ActivatedPlugins.ContainsKey(plugin)) + { + Log.Warning("Trying to activate plugin {0} that is already activated", plugin); + return; + } + + var pluginMetadata = this.FoolsPlugins.FirstOrDefault(x => x.InternalName == plugin); + if (pluginMetadata == null) + { + Log.Warning("Trying to activate plugin {0} that does not exist", plugin); + return; + } + + var pluginInstance = (IFoolsPlugin)Activator.CreateInstance(pluginMetadata.Type); + this.ActivatedPlugins.Add(plugin, pluginInstance); + } + + public bool IsPluginActivated(string plugin) + { + return this.ActivatedPlugins.ContainsKey(plugin); + } + + public void DeactivatePlugin(string plugin) + { + if (!this.ActivatedPlugins.ContainsKey(plugin)) + { + Log.Warning("Trying to deactivate plugin {0} that is not activated", plugin); + return; + } + + var pluginInstance = this.ActivatedPlugins[plugin]; + pluginInstance.Dispose(); + this.ActivatedPlugins.Remove(plugin); + } + + public void Dispose() + { + foreach (var plugin in this.ActivatedPlugins.Values) + { + plugin.Dispose(); + } + + this.ActivatedPlugins.Clear(); + } +} diff --git a/Dalamud/Fools/FoolsPluginMetadata.cs b/Dalamud/Fools/FoolsPluginMetadata.cs new file mode 100644 index 000000000..25c095a60 --- /dev/null +++ b/Dalamud/Fools/FoolsPluginMetadata.cs @@ -0,0 +1,25 @@ +using System; + +namespace Dalamud.Fools; + +public class FoolsPluginMetadata +{ + public string Name { get; } + + public string InternalName { get; } + + public string Description { get; } + + public string Author { get; } + + public Type Type { get; } + + public FoolsPluginMetadata(string name, string internalName, string description, string author, Type type) + { + this.Name = name; + this.InternalName = internalName; + this.Description = description; + this.Author = author; + this.Type = type; + } +} diff --git a/Dalamud/Fools/IFoolsPlugin.cs b/Dalamud/Fools/IFoolsPlugin.cs new file mode 100644 index 000000000..f2957508e --- /dev/null +++ b/Dalamud/Fools/IFoolsPlugin.cs @@ -0,0 +1,8 @@ +using Dalamud.Plugin; + +namespace Dalamud.Fools; + +public interface IFoolsPlugin : IDalamudPlugin +{ + public void DrawUI() { } +} diff --git a/Dalamud/Fools/Plugins/TestFoolPlugin.cs b/Dalamud/Fools/Plugins/TestFoolPlugin.cs new file mode 100644 index 000000000..1cf2d1d94 --- /dev/null +++ b/Dalamud/Fools/Plugins/TestFoolPlugin.cs @@ -0,0 +1,16 @@ +namespace Dalamud.Fools.Plugins; + +public class TestFoolPlugin : IFoolsPlugin +{ + public string Name => "TestFoolPlugin"; + + public string Description => "TestFoolPlugin"; + + public string InternalName => "TestFoolPlugin"; + + public string Author => "NotNite"; + + public TestFoolPlugin() { } + + public void Dispose() { } +} diff --git a/Dalamud/Interface/Internal/PluginCategoryManager.cs b/Dalamud/Interface/Internal/PluginCategoryManager.cs index 06e306c50..1a8acfa3f 100644 --- a/Dalamud/Interface/Internal/PluginCategoryManager.cs +++ b/Dalamud/Interface/Internal/PluginCategoryManager.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; - using CheapLoc; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Internal.Types; @@ -23,11 +22,13 @@ internal class PluginCategoryManager { new(0, "special.all", () => Locs.Category_All), new(1, "special.isTesting", () => Locs.Category_IsTesting, CategoryInfo.AppearCondition.DoPluginTest), - new(2, "special.availableForTesting", () => Locs.Category_AvailableForTesting, CategoryInfo.AppearCondition.DoPluginTest), + new(2, "special.availableForTesting", () => Locs.Category_AvailableForTesting, + CategoryInfo.AppearCondition.DoPluginTest), new(10, "special.devInstalled", () => Locs.Category_DevInstalled), new(11, "special.devIconTester", () => Locs.Category_IconTester), new(12, "special.dalamud", () => Locs.Category_Dalamud), new(13, "special.plugins", () => Locs.Category_Plugins), + new(14, "special.alternateReality", () => "Alternate Reality"), new(FirstTagBasedCategoryId + 0, "other", () => Locs.Category_Other), new(FirstTagBasedCategoryId + 1, "jobs", () => Locs.Category_Jobs), new(FirstTagBasedCategoryId + 2, "ui", () => Locs.Category_UI), @@ -36,7 +37,6 @@ internal class PluginCategoryManager new(FirstTagBasedCategoryId + 5, "sound", () => Locs.Category_Sound), new(FirstTagBasedCategoryId + 6, "social", () => Locs.Category_Social), new(FirstTagBasedCategoryId + 7, "utility", () => Locs.Category_Utility), - // order doesn't matter, all tag driven categories should have Id >= FirstTagBasedCategoryId }; @@ -46,6 +46,7 @@ internal class PluginCategoryManager new(GroupKind.Installed, () => Locs.Group_Installed, 0, 1), new(GroupKind.Available, () => Locs.Group_Available, 0), new(GroupKind.Changelog, () => Locs.Group_Changelog, 0, 12, 13), + new(GroupKind.AlternateReality, () => "Alternate Reality", 14), // order important, used for drawing, keep in sync with defaults for currentGroupIdx }; @@ -81,6 +82,11 @@ internal class PluginCategoryManager /// UI group: changelog of plugins. /// Changelog, + + /// + /// April fools! + /// + AlternateReality } /// @@ -167,7 +173,8 @@ internal class PluginCategoryManager foreach (var tag in pluginCategoryTags) { // only tags from whitelist can be accepted - var matchIdx = Array.FindIndex(this.CategoryList, x => x.Tag.Equals(tag, StringComparison.InvariantCultureIgnoreCase)); + var matchIdx = Array.FindIndex(this.CategoryList, + x => x.Tag.Equals(tag, StringComparison.InvariantCultureIgnoreCase)); if (matchIdx >= 0) { var categoryId = this.CategoryList[matchIdx].CategoryId; @@ -235,7 +242,9 @@ internal class PluginCategoryManager } else { - var selectedCategoryInfo = Array.Find(this.categoryList, x => x.CategoryId == groupInfo.Categories[this.currentCategoryIdx]); + var selectedCategoryInfo = Array.Find(this.categoryList, + x => x.CategoryId == + groupInfo.Categories[this.currentCategoryIdx]); foreach (var plugin in plugins) { @@ -330,7 +339,8 @@ internal class PluginCategoryManager /// Tag to match. /// Function returning localized name of category. /// Condition to be checked when deciding whether this category should be shown. - public CategoryInfo(int categoryId, string tag, Func nameFunc, AppearCondition condition = AppearCondition.None) + public CategoryInfo( + int categoryId, string tag, Func nameFunc, AppearCondition condition = AppearCondition.None) { this.CategoryId = categoryId; this.Tag = tag; @@ -403,7 +413,8 @@ internal class PluginCategoryManager public string Name => this.nameFunc(); } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "locs")] + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", + Justification = "locs")] internal static class Locs { #region UI groups @@ -424,9 +435,11 @@ internal class PluginCategoryManager public static string Category_IsTesting => Loc.Localize("InstallerCategoryIsTesting", "Currently Testing"); - public static string Category_AvailableForTesting => Loc.Localize("InstallerCategoryAvailableForTesting", "Testing Available"); + public static string Category_AvailableForTesting => + Loc.Localize("InstallerCategoryAvailableForTesting", "Testing Available"); - public static string Category_DevInstalled => Loc.Localize("InstallerInstalledDevPlugins", "Installed Dev Plugins"); + public static string Category_DevInstalled => + Loc.Localize("InstallerInstalledDevPlugins", "Installed Dev Plugins"); public static string Category_IconTester => "Image/Icon Tester"; diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index c7847f58e..bbc8d1d7c 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using CheapLoc; using Dalamud.Configuration.Internal; +using Dalamud.Fools; using Dalamud.Game.Command; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; @@ -1236,6 +1237,11 @@ internal class PluginInstallerWindow : Window, IDisposable } break; + + case PluginCategoryManager.GroupKind.AlternateReality: + this.DrawAlternateRealityPlugins(); + break; + default: this.DrawAvailablePluginList(); break; @@ -1244,6 +1250,41 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PopStyleVar(); } + private void DrawAlternateRealityPlugins() + { + var manager = Service.Get(); + + foreach (var plugin in manager.FoolsPlugins) + { + // dropdown + if (ImGui.CollapsingHeader($"{plugin.Name}##AprilFools_{plugin.Name}")) + { + ImGui.Indent(); + ImGui.Text(plugin.Name); + ImGui.SameLine(); + + ImGui.TextColored(ImGuiColors.DalamudGrey3, $" by {plugin.Author}"); + + ImGui.TextWrapped(plugin.Description); + + if (manager.IsPluginActivated(plugin.InternalName)) + { + if (ImGui.Button("Disable")) + { + manager.DeactivatePlugin(plugin.InternalName); + } + } + else + { + if (ImGui.Button("Install")) + { + manager.ActivatePlugin(plugin.InternalName); + } + } + } + } + } + private void DrawImageTester() { var sectionSize = ImGuiHelpers.GlobalScale * 66; From 7d5dd11a429186e94572bf36413d147cedfbc5cd Mon Sep 17 00:00:00 2001 From: NotNite Date: Sat, 18 Mar 2023 19:57:19 -0400 Subject: [PATCH 02/34] garbage UI code --- Dalamud/Fools/FoolsManager.cs | 26 ++++++++++++++++++++----- Dalamud/Fools/Plugins/TestFoolPlugin.cs | 14 ++++++++++++- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 8d08e5c39..d71b416e9 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Dalamud.Fools.Plugins; +using Dalamud.Interface; using Dalamud.Logging.Internal; namespace Dalamud.Fools; @@ -15,12 +16,17 @@ internal class FoolsManager : IDisposable, IServiceType { public readonly List FoolsPlugins = new(); public readonly Dictionary ActivatedPlugins = new(); - + private static readonly ModuleLog Log = new("FOOLS"); + private UiBuilder uiBuilder; + [ServiceManager.ServiceConstructor] private FoolsManager() { + this.uiBuilder = new UiBuilder("fools"); + this.uiBuilder.Draw += this.DrawUI; + // reflect over all IFoolsPlugin implementations this.FoolsPlugins = new List { @@ -47,6 +53,18 @@ internal class FoolsManager : IDisposable, IServiceType this.ActivatedPlugins.Add(plugin, pluginInstance); } + public void Dispose() + { + foreach (var plugin in this.ActivatedPlugins.Values) + { + plugin.Dispose(); + } + + this.ActivatedPlugins.Clear(); + + ((IDisposable)this.uiBuilder).Dispose(); + } + public bool IsPluginActivated(string plugin) { return this.ActivatedPlugins.ContainsKey(plugin); @@ -65,13 +83,11 @@ internal class FoolsManager : IDisposable, IServiceType this.ActivatedPlugins.Remove(plugin); } - public void Dispose() + private void DrawUI() { foreach (var plugin in this.ActivatedPlugins.Values) { - plugin.Dispose(); + plugin.DrawUI(); } - - this.ActivatedPlugins.Clear(); } } diff --git a/Dalamud/Fools/Plugins/TestFoolPlugin.cs b/Dalamud/Fools/Plugins/TestFoolPlugin.cs index 1cf2d1d94..4eb36ed7a 100644 --- a/Dalamud/Fools/Plugins/TestFoolPlugin.cs +++ b/Dalamud/Fools/Plugins/TestFoolPlugin.cs @@ -1,4 +1,6 @@ -namespace Dalamud.Fools.Plugins; +using ImGuiNET; + +namespace Dalamud.Fools.Plugins; public class TestFoolPlugin : IFoolsPlugin { @@ -12,5 +14,15 @@ public class TestFoolPlugin : IFoolsPlugin public TestFoolPlugin() { } + public void DrawUI() + { + if (ImGui.Begin("Nuts")) + { + ImGui.Text("balls"); + } + + ImGui.End(); + } + public void Dispose() { } } From 7d50ab3049d11d839fa8a7b1392e66e6847ce515 Mon Sep 17 00:00:00 2001 From: NotNite Date: Sat, 18 Mar 2023 20:03:15 -0400 Subject: [PATCH 03/34] whoops --- Dalamud/Fools/FoolsManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index d71b416e9..4d58b7a03 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -27,7 +27,7 @@ internal class FoolsManager : IDisposable, IServiceType this.uiBuilder = new UiBuilder("fools"); this.uiBuilder.Draw += this.DrawUI; - // reflect over all IFoolsPlugin implementations + // reflect over all IFoolsPlugin implementations sometime(?) this.FoolsPlugins = new List { new("Test Fool Plugin", "TestFoolPlugin", "this is a test", "NotNite", typeof(TestFoolPlugin)), From d6ffe6fcdc8802defc0106a072dcbe2e97d86150 Mon Sep 17 00:00:00 2001 From: NotNite Date: Sat, 18 Mar 2023 22:00:40 -0400 Subject: [PATCH 04/34] testing this shit is nightmare fuel --- Dalamud/Fools/FoolsManager.cs | 4 +- Dalamud/Fools/IFoolsPlugin.cs | 6 +-- Dalamud/Fools/Plugins/PixelImperfectPlugin.cs | 53 +++++++++++++++++++ Dalamud/Fools/Plugins/TestFoolPlugin.cs | 2 +- .../PluginInstaller/PluginInstallerWindow.cs | 8 +-- 5 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 Dalamud/Fools/Plugins/PixelImperfectPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 4d58b7a03..73c3e0694 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -31,6 +31,8 @@ internal class FoolsManager : IDisposable, IServiceType this.FoolsPlugins = new List { new("Test Fool Plugin", "TestFoolPlugin", "this is a test", "NotNite", typeof(TestFoolPlugin)), + new("Pixel Imperfect", "PixelImperfectPlugin", "Whoops... we messed up the math on that one.", "Halpo", + typeof(PixelImperfectPlugin)), }; } @@ -87,7 +89,7 @@ internal class FoolsManager : IDisposable, IServiceType { foreach (var plugin in this.ActivatedPlugins.Values) { - plugin.DrawUI(); + plugin.DrawUi(); } } } diff --git a/Dalamud/Fools/IFoolsPlugin.cs b/Dalamud/Fools/IFoolsPlugin.cs index f2957508e..5764dd78d 100644 --- a/Dalamud/Fools/IFoolsPlugin.cs +++ b/Dalamud/Fools/IFoolsPlugin.cs @@ -1,8 +1,8 @@ -using Dalamud.Plugin; +using System; namespace Dalamud.Fools; -public interface IFoolsPlugin : IDalamudPlugin +public interface IFoolsPlugin : IDisposable { - public void DrawUI() { } + public void DrawUi() { } } diff --git a/Dalamud/Fools/Plugins/PixelImperfectPlugin.cs b/Dalamud/Fools/Plugins/PixelImperfectPlugin.cs new file mode 100644 index 000000000..7ac21624f --- /dev/null +++ b/Dalamud/Fools/Plugins/PixelImperfectPlugin.cs @@ -0,0 +1,53 @@ +using System; +using System.Numerics; +using Dalamud.Game.ClientState; +using Dalamud.Game.Gui; +using Dalamud.Interface; +using ImGuiNET; +using static ImGuiNET.ImGuiWindowFlags; + +namespace Dalamud.Fools.Plugins; + +public class PixelImperfectPlugin : IFoolsPlugin +{ + private ClientState clientState; + private GameGui gameGui; + + public PixelImperfectPlugin() + { + this.clientState = Service.Get(); + this.gameGui = Service.Get(); + } + + public void DrawUi() + { + if (this.clientState.LocalPlayer == null) return; + + // Copied directly from PixelPerfect + ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(0, 0)); + ImGuiHelpers.ForceNextWindowMainViewport(); + ImGuiHelpers.SetNextWindowPosRelativeMainViewport(new Vector2(0, 0)); + ImGui.Begin("Canvas", NoInputs | NoNav | NoTitleBar | NoScrollbar | NoBackground); + ImGui.SetWindowSize(ImGui.GetIO().DisplaySize); + + var xOffset = Math.Sin(Environment.TickCount / 500.0); + var yOffset = Math.Sin(Environment.TickCount / 1500.0); + var actorPos = this.clientState.LocalPlayer.Position; + + this.gameGui.WorldToScreen( + new Vector3(actorPos.X + (float)xOffset, actorPos.Y, actorPos.Z + (float)yOffset), + out var pos); + + ImGui.GetWindowDrawList().AddCircle( + new Vector2(pos.X, pos.Y), + 2, + ImGui.GetColorU32(new Vector4(255, 255, 255, 255)), + 100, + 10); + + ImGui.End(); + ImGui.PopStyleVar(); + } + + public void Dispose() { } +} diff --git a/Dalamud/Fools/Plugins/TestFoolPlugin.cs b/Dalamud/Fools/Plugins/TestFoolPlugin.cs index 4eb36ed7a..57b36cc46 100644 --- a/Dalamud/Fools/Plugins/TestFoolPlugin.cs +++ b/Dalamud/Fools/Plugins/TestFoolPlugin.cs @@ -14,7 +14,7 @@ public class TestFoolPlugin : IFoolsPlugin public TestFoolPlugin() { } - public void DrawUI() + public void DrawUi() { if (ImGui.Begin("Nuts")) { diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index bbc8d1d7c..42e016f57 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -1257,7 +1257,7 @@ internal class PluginInstallerWindow : Window, IDisposable foreach (var plugin in manager.FoolsPlugins) { // dropdown - if (ImGui.CollapsingHeader($"{plugin.Name}##AprilFools_{plugin.Name}")) + if (ImGui.CollapsingHeader($"{plugin.Name}##AprilFools_Header_{plugin.Name}")) { ImGui.Indent(); ImGui.Text(plugin.Name); @@ -1269,18 +1269,20 @@ internal class PluginInstallerWindow : Window, IDisposable if (manager.IsPluginActivated(plugin.InternalName)) { - if (ImGui.Button("Disable")) + if (ImGui.Button($"Disable##AprilFools_Disable_{plugin.Name}")) { manager.DeactivatePlugin(plugin.InternalName); } } else { - if (ImGui.Button("Install")) + if (ImGui.Button($"Install##AprilFools_Enable_{plugin.Name}")) { manager.ActivatePlugin(plugin.InternalName); } } + + ImGui.Unindent(); } } } From 06fea85be3f3ba1701f513d9ca96051d0638c583 Mon Sep 17 00:00:00 2001 From: Bernardo Lopes Date: Sun, 19 Mar 2023 00:48:40 -0300 Subject: [PATCH 05/34] Create DailyLifeDutyPlugin for AU Dalamud. --- Dalamud/Fools/FoolsManager.cs | 1 + Dalamud/Fools/Helper/Chat.cs | 32 ++++++++++ Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs | 62 ++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 Dalamud/Fools/Helper/Chat.cs create mode 100644 Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 73c3e0694..b8660876d 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -33,6 +33,7 @@ internal class FoolsManager : IDisposable, IServiceType new("Test Fool Plugin", "TestFoolPlugin", "this is a test", "NotNite", typeof(TestFoolPlugin)), new("Pixel Imperfect", "PixelImperfectPlugin", "Whoops... we messed up the math on that one.", "Halpo", typeof(PixelImperfectPlugin)), + new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", "MidoriKami", typeof(DailyLifeDutyPlugin)), }; } diff --git a/Dalamud/Fools/Helper/Chat.cs b/Dalamud/Fools/Helper/Chat.cs new file mode 100644 index 000000000..54e0803c6 --- /dev/null +++ b/Dalamud/Fools/Helper/Chat.cs @@ -0,0 +1,32 @@ +using Dalamud.Game.Gui; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; + +namespace Dalamud.Fools.Helper; + +// Copied from KamiLib's Chat +// https://github.com/MidoriKami/KamiLib/blob/master/ChatCommands/Chat.cs +internal static class Chat +{ + public static void Print(string pluginName, string tag, string message) => Service.Get().Print(GetBaseString(pluginName, tag, message).BuiltString); + + private static SeStringBuilder GetBaseString(string pluginName, string tag, string message, DalamudLinkPayload? payload = null) + { + if (payload is null) + { + return new SeStringBuilder() + .AddUiForeground($"[{pluginName}] ", 45) + .AddUiForeground($"[{tag}] ", 62) + .AddText(message); + } + else + { + return new SeStringBuilder() + .AddUiForeground($"[{pluginName}] ", 45) + .AddUiForeground($"[{tag}] ", 62) + .Add(payload) + .AddUiForeground(message, 35) + .Add(RawPayload.LinkTerminator); + } + } +} diff --git a/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs b/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs new file mode 100644 index 000000000..35ea47a99 --- /dev/null +++ b/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Dalamud.Fools.Helper; +using Dalamud.Game; + +namespace Dalamud.Fools.Plugins; + +public class DailyLifeDutyPlugin : IFoolsPlugin +{ + private const string PluginName = "DailyLifeDuty"; + + private static readonly List Duties = new[] + { + new Duty("Dishes", i => $"{i} dishes to be cleaned"), + new Duty("Taxes", _ => "Taxes need to be filed"), + new Duty("Pets", i => $"{i} dogs waiting to be pet"), + new Duty("Garbage", i => $"{i} garbage bags to be put out"), + }.ToList(); + + private long lastMessage; + + public DailyLifeDutyPlugin() + { + Service.Get().Update += this.OnUpdate; + this.EmitDutyReminder(); + } + + public void Dispose() + { + Service.Get().Update -= this.OnUpdate; + } + + private void OnUpdate(Framework framework) + { + if (DateTimeOffset.UtcNow.ToUnixTimeSeconds() - this.lastMessage > 60 * 5) + { + this.EmitDutyReminder(); + } + } + + private void EmitDutyReminder() + { + var duty = Duties[Random.Shared.Next(Duties.Count)]; + Chat.Print(PluginName, duty.Tag, duty.Message(Random.Shared.Next(20))); + this.lastMessage = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + } + + private class Duty + { + public Duty(string tag, Func message) + { + this.Tag = tag; + this.Message = message; + } + + internal string Tag { get; init; } + + internal Func Message { get; init; } + } +} From 519580a4d17343037b7eef0d2502d71c198b3445 Mon Sep 17 00:00:00 2001 From: NotNite Date: Sun, 19 Mar 2023 11:16:06 -0400 Subject: [PATCH 06/34] Remove TestFoolPlugin --- Dalamud/Fools/FoolsManager.cs | 1 - Dalamud/Fools/Plugins/TestFoolPlugin.cs | 28 ------------------------- 2 files changed, 29 deletions(-) delete mode 100644 Dalamud/Fools/Plugins/TestFoolPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index b8660876d..fbb4fbf92 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -30,7 +30,6 @@ internal class FoolsManager : IDisposable, IServiceType // reflect over all IFoolsPlugin implementations sometime(?) this.FoolsPlugins = new List { - new("Test Fool Plugin", "TestFoolPlugin", "this is a test", "NotNite", typeof(TestFoolPlugin)), new("Pixel Imperfect", "PixelImperfectPlugin", "Whoops... we messed up the math on that one.", "Halpo", typeof(PixelImperfectPlugin)), new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", "MidoriKami", typeof(DailyLifeDutyPlugin)), diff --git a/Dalamud/Fools/Plugins/TestFoolPlugin.cs b/Dalamud/Fools/Plugins/TestFoolPlugin.cs deleted file mode 100644 index 57b36cc46..000000000 --- a/Dalamud/Fools/Plugins/TestFoolPlugin.cs +++ /dev/null @@ -1,28 +0,0 @@ -using ImGuiNET; - -namespace Dalamud.Fools.Plugins; - -public class TestFoolPlugin : IFoolsPlugin -{ - public string Name => "TestFoolPlugin"; - - public string Description => "TestFoolPlugin"; - - public string InternalName => "TestFoolPlugin"; - - public string Author => "NotNite"; - - public TestFoolPlugin() { } - - public void DrawUi() - { - if (ImGui.Begin("Nuts")) - { - ImGui.Text("balls"); - } - - ImGui.End(); - } - - public void Dispose() { } -} From af99bbd507b3c33169b928952e867eabcea0f4a0 Mon Sep 17 00:00:00 2001 From: NotNite Date: Sun, 19 Mar 2023 11:23:34 -0400 Subject: [PATCH 07/34] Add a very nice present --- Dalamud/Fools/FoolsManager.cs | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index fbb4fbf92..a2c52781b 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -8,6 +8,44 @@ using Dalamud.Logging.Internal; namespace Dalamud.Fools; +// . ///*,.. .,*/((#######((((((((((((#(((########(*,****,,....,**/// +// . (//*,,. ./(#%%%%%%%##((((/////(((((###(#####%#(/**,......,,**/( +// (*((/*,,,/###%%%%%%%%%%%#((///((((##%%%%%%%%####%#(//,. ..,,*/(* +// #((/*/(((((#%%%%&&%%%%%##(/(((((#%%%%&%%%%%%%##(((((/,...,,*/(#% +// %,&%#######%%%%%%%%%%%%%%%%###(####%%%%%%&&&%%%%%%#((((((((/**/(#&% +// ,,#&%%####%%%%%(//##%%%%%%%%%%#%%%%%%%%%%%%%%%%%%%%%%%%%###(####((#%# +// ,.%&%%%%%%%%%#/. *#%%%&&&&&&&&&%&&&%%%%%%%%%#*...*(##%%####((##%%%% +// ..%&&%%%%%%%%#(* ,%&&&&&&&&&&&&&&&&&&&%%%(/, . *(#%%#######%%% +// /#&&&%%%%%%%%%%#, *#&&&&&&&&&&&&&&&&&&&&&&&&&&%(, , ,(####%%%%#### +// %&&&&%%%%&%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%*. /#%%%%%#%%%%%% +// (&&%%%%%&&&&&&&&&&&&&%%%%%%%%%%%%##%%&&&&&&&&&&&&&&%%%%###%%%%%%%%%%%%%### +// *(@&%%%%%&&&&&%%%%%#(#############%%%%%%%&&&&&&&&&&&&&&&%%&%&&&%%%%%%%%%%%%% +// */&&%%%#((//******/********/(((((((/*/(#%%&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%% +// ((&%/,................,,,,,,,,*,,,,,*,,,,*(%%%%&&&&&&&&&&&&&&&&&%%%%%%%%%%%% +// . , ...............,,*(%%&&&&&&&&&&&&&&&&&&%%%%%%%%%% +// ., .,,,**/(#%%%&&&&&&&&&&&&&&%%%%%%%%% +// . ..,,,**/#%%%&&&&&&&&&&&&&&%%%%%%%% +// , .,*/#%%%%&&&&&&&&&&&&&%%%%%%%% +// ,,. ./(#%%%&&&&&&&&%&&&%%##%%%%% +// ((%&# ,(#%%%%&&&&&&&%%%&%%%%%%%%% +// #,%%/ .*#%%%%&&&&&&&&%&%%###%%%%% +// @(%%( .,(%%%%%%%&&&&&&&%%#%%%%%%%% +// /%%%* ./#%%%%%%%%%&&&&&%##%%%%%%%( +// #*&%%#. ..,(#%%%%%%%%%%&&&&&%%%%%%%#((, +// *(%%#/. .,**(#####%%%%%%%%%%%%%%%%%#**.. +// #((#%##/. ,,*(####%%%%%%%%%%%&&%%%#(, +// **###((*. ,(*///((####%%%%%%%&&&%(/., +// .,(###(//*,. ..,*//(((((##%%%%%%%%%%%%#%. +// . (#((/**,,.. ..,*//*/////(##%%%%%%%%%(#(* +// */(((/////,.. .....,,,,***///####%%%%%%%#. +// //*((((*,.. ...,...,,,.,//((((##%%%%%%%,/ +// * //(/******,,,.,,..,,,****/*///((((#(###(/##(, +// +// April fools! <3 +// This was a group effort in one week lead by: +// NotNite: FoolsManager, Pixel Imperfect +// Berna: DailyLifeDuty + /// /// Manager for all the IFoolsPlugin instances. /// From c60bb99e77b461953b2beb02fcdb578a683a61a6 Mon Sep 17 00:00:00 2001 From: NotNite Date: Sun, 19 Mar 2023 13:54:38 -0400 Subject: [PATCH 08/34] Add window that opens on login --- .../Internal/DalamudConfiguration.cs | 2 + Dalamud/Fools/FoolsManager.cs | 40 +- Dalamud/Fools/FoolsWindow.cs | 61 ++ .../Interface/Internal/DalamudInterface.cs | 16 + .../PluginInstaller/PluginInstallerWindow.cs | 622 ++++++++++++------ 5 files changed, 541 insertions(+), 200 deletions(-) create mode 100644 Dalamud/Fools/FoolsWindow.cs diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs index d2f8c4aa1..fe1f1c3f9 100644 --- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs +++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs @@ -371,6 +371,8 @@ internal sealed class DalamudConfiguration : IServiceType /// public double UiBuilderHitch { get; set; } = 100; + public bool HasSeenFools23 { get; set; } = false; + /// /// Load a configuration from the provided path. /// diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index a2c52781b..3e74080df 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -2,8 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using Dalamud.Configuration.Internal; using Dalamud.Fools.Plugins; +using Dalamud.Game.ClientState; using Dalamud.Interface; +using Dalamud.Interface.Internal; using Dalamud.Logging.Internal; namespace Dalamud.Fools; @@ -58,22 +61,28 @@ internal class FoolsManager : IDisposable, IServiceType private static readonly ModuleLog Log = new("FOOLS"); private UiBuilder uiBuilder; + private ClientState clientState; [ServiceManager.ServiceConstructor] private FoolsManager() { this.uiBuilder = new UiBuilder("fools"); - this.uiBuilder.Draw += this.DrawUI; + this.uiBuilder.Draw += this.DrawUi; + + this.clientState = Service.Get(); + this.clientState.Login += this.ClientStateOnLogin; // reflect over all IFoolsPlugin implementations sometime(?) this.FoolsPlugins = new List { new("Pixel Imperfect", "PixelImperfectPlugin", "Whoops... we messed up the math on that one.", "Halpo", typeof(PixelImperfectPlugin)), - new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", "MidoriKami", typeof(DailyLifeDutyPlugin)), + new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", + "MidoriKami", typeof(DailyLifeDutyPlugin)), }; } + public void ActivatePlugin(string plugin) { if (this.ActivatedPlugins.ContainsKey(plugin)) @@ -101,8 +110,8 @@ internal class FoolsManager : IDisposable, IServiceType } this.ActivatedPlugins.Clear(); - ((IDisposable)this.uiBuilder).Dispose(); + this.clientState.Login -= this.ClientStateOnLogin; } public bool IsPluginActivated(string plugin) @@ -123,11 +132,34 @@ internal class FoolsManager : IDisposable, IServiceType this.ActivatedPlugins.Remove(plugin); } - private void DrawUI() + private void DrawUi() { foreach (var plugin in this.ActivatedPlugins.Values) { plugin.DrawUi(); } } + + private void ClientStateOnLogin(object? o, EventArgs e) + { + var dalamudConfig = Service.Get(); + +#if !DEBUG + if (DateTime.Now is not { Month: 4, Day: 1 }) + { + return; + } +#endif + + if (dalamudConfig.HasSeenFools23) + { + return; + } + + var di = Service.Get(); + di.OpenFoolsWindow(); + + dalamudConfig.HasSeenFools23 = true; + dalamudConfig.QueueSave(); + } } diff --git a/Dalamud/Fools/FoolsWindow.cs b/Dalamud/Fools/FoolsWindow.cs new file mode 100644 index 000000000..93663f67d --- /dev/null +++ b/Dalamud/Fools/FoolsWindow.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using System.Numerics; +using Dalamud.Interface; +using Dalamud.Interface.Internal; +using Dalamud.Interface.Windowing; +using ImGuiNET; +using ImGuiScene; + +namespace Dalamud.Fools; + +// Stolen from ChangelogWindow +public class FoolsWindow : Window, IDisposable { + private readonly TextureWrap logoTexture; + + public FoolsWindow() + : base("Introducing Alternate Reality Dalamud", ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoResize) + { + this.Namespace = "FoolsWindow"; + + this.Size = new Vector2(885, 463); + this.SizeCondition = ImGuiCond.Appearing; + + var interfaceManager = Service.Get(); + var dalamud = Service.Get(); + + this.logoTexture = + interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "logo.png"))!; + } + + public override void Draw() + { + var imgCursor = ImGui.GetCursorPos(); + ImGui.TextWrapped(@" +A team of scientists and plugin developers have collaborated to create a new +version of Dalamud that includes plugins from other realities. + +With our high tech systems, the plugin installer will now offer new, unique +plugins with endless possibilities. Open the ""Alternate Reality"" tab in the +plugin installer to see what's available. + +We hope you enjoy this new version of Dalamud, and we look forward to your feedback. +".Trim()); + + if (ImGui.Button("Open the plugin installer")) + { + var di = Service.Get(); + di.OpenPluginInstallerFools(); + } + + imgCursor.X += 500; + ImGui.SetCursorPos(imgCursor); + + ImGui.Image(this.logoTexture.ImGuiHandle, new Vector2(100)); + } + + public void Dispose() + { + this.logoTexture.Dispose(); + } +} diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index 3da633c86..c94ec7051 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; using CheapLoc; using Dalamud.Configuration.Internal; +using Dalamud.Fools; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.Gui; using Dalamud.Game.Internal; @@ -62,6 +63,7 @@ internal class DalamudInterface : IDisposable, IServiceType private readonly ProfilerWindow profilerWindow; private readonly BranchSwitcherWindow branchSwitcherWindow; private readonly HitchSettingsWindow hitchSettingsWindow; + private readonly FoolsWindow foolsWindow; private readonly TextureWrap logoTexture; private readonly TextureWrap tsmLogoTexture; @@ -110,6 +112,7 @@ internal class DalamudInterface : IDisposable, IServiceType this.profilerWindow = new ProfilerWindow() { IsOpen = false }; this.branchSwitcherWindow = new BranchSwitcherWindow() { IsOpen = false }; this.hitchSettingsWindow = new HitchSettingsWindow() { IsOpen = false }; + this.foolsWindow = new FoolsWindow() { IsOpen = false }; this.WindowSystem.AddWindow(this.changelogWindow); this.WindowSystem.AddWindow(this.colorDemoWindow); @@ -127,6 +130,7 @@ internal class DalamudInterface : IDisposable, IServiceType this.WindowSystem.AddWindow(this.profilerWindow); this.WindowSystem.AddWindow(this.branchSwitcherWindow); this.WindowSystem.AddWindow(this.hitchSettingsWindow); + this.WindowSystem.AddWindow(this.foolsWindow); ImGuiManagedAsserts.AssertsEnabled = configuration.AssertsEnabledAtStartup; this.isImGuiDrawDevMenu = this.isImGuiDrawDevMenu || configuration.DevBarOpenAtStartup; @@ -338,6 +342,18 @@ internal class DalamudInterface : IDisposable, IServiceType this.branchSwitcherWindow.BringToFront(); } + public void OpenFoolsWindow() + { + this.foolsWindow.IsOpen = true; + this.foolsWindow.BringToFront(); + } + + public void OpenPluginInstallerFools() + { + this.pluginWindow.OpenFools(); + this.pluginWindow.BringToFront(); + } + #endregion #region Close diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 42e016f57..451c9f2f2 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Numerics; using System.Threading; using System.Threading.Tasks; - using CheapLoc; using Dalamud.Configuration.Internal; using Dalamud.Fools; @@ -115,7 +114,9 @@ internal class PluginInstallerWindow : Window, IDisposable /// An instance of class. public PluginInstallerWindow(PluginImageCache imageCache) : base( - Locs.WindowTitle + (Service.Get().DoPluginTest ? Locs.WindowTitleMod_Testing : string.Empty) + "###XlPluginInstaller", + Locs.WindowTitle + + (Service.Get().DoPluginTest ? Locs.WindowTitleMod_Testing : string.Empty) + + "###XlPluginInstaller", ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollbar) { this.IsOpen = true; @@ -264,6 +265,13 @@ internal class PluginInstallerWindow : Window, IDisposable this.IsOpen = true; } + public void OpenFools() + { + this.categoryManager.CurrentGroupIdx = 4; + this.categoryManager.CurrentCategoryIdx = 0; + this.IsOpen = true; + } + /// /// Open the window on the plugin changelogs. /// @@ -340,45 +348,45 @@ internal class PluginInstallerWindow : Window, IDisposable ImGuiHelpers.CenteredText("Installing plugin..."); break; case LoadingIndicatorKind.Manager: + { + if (pluginManager.PluginsReady && !pluginManager.ReposReady) { - if (pluginManager.PluginsReady && !pluginManager.ReposReady) - { - ImGuiHelpers.CenteredText("Loading repositories..."); - } - else if (!pluginManager.PluginsReady && pluginManager.ReposReady) - { - ImGuiHelpers.CenteredText("Loading installed plugins..."); - } - else - { - ImGuiHelpers.CenteredText("Loading repositories and plugins..."); - } - - var currentProgress = 0; - var total = 0; - - var pendingRepos = pluginManager.Repos.ToArray() - .Where(x => (x.State != PluginRepositoryState.Success && - x.State != PluginRepositoryState.Fail) && - x.IsEnabled) - .ToArray(); - var allRepoCount = - pluginManager.Repos.Count(x => x.State != PluginRepositoryState.Fail && x.IsEnabled); - - foreach (var repo in pendingRepos) - { - ImGuiHelpers.CenteredText($"{repo.PluginMasterUrl}: {repo.State}"); - } - - currentProgress += allRepoCount - pendingRepos.Length; - total += allRepoCount; - - if (currentProgress != total) - { - ImGui.SetCursorPosX(windowSize.X / 3); - ImGui.ProgressBar(currentProgress / (float)total, new Vector2(windowSize.X / 3, 50)); - } + ImGuiHelpers.CenteredText("Loading repositories..."); } + else if (!pluginManager.PluginsReady && pluginManager.ReposReady) + { + ImGuiHelpers.CenteredText("Loading installed plugins..."); + } + else + { + ImGuiHelpers.CenteredText("Loading repositories and plugins..."); + } + + var currentProgress = 0; + var total = 0; + + var pendingRepos = pluginManager.Repos.ToArray() + .Where(x => (x.State != PluginRepositoryState.Success && + x.State != PluginRepositoryState.Fail) && + x.IsEnabled) + .ToArray(); + var allRepoCount = + pluginManager.Repos.Count(x => x.State != PluginRepositoryState.Fail && x.IsEnabled); + + foreach (var repo in pendingRepos) + { + ImGuiHelpers.CenteredText($"{repo.PluginMasterUrl}: {repo.State}"); + } + + currentProgress += allRepoCount - pendingRepos.Length; + total += allRepoCount; + + if (currentProgress != total) + { + ImGui.SetCursorPosX(windowSize.X / 3); + ImGui.ProgressBar(currentProgress / (float)total, new Vector2(windowSize.X / 3, 50)); + } + } break; default: @@ -420,8 +428,10 @@ internal class PluginInstallerWindow : Window, IDisposable (Locs.SortBy_EnabledDisabled, PluginSortKind.EnabledDisabled), }; var longestSelectableWidth = sortSelectables.Select(t => ImGui.CalcTextSize(t.Localization).X).Max(); - var selectableWidth = longestSelectableWidth + (style.FramePadding.X * 2); // This does not include the label - var sortSelectWidth = selectableWidth + sortByTextWidth + style.ItemInnerSpacing.X; // Item spacing between the selectable and the label + var selectableWidth = longestSelectableWidth + (style.FramePadding.X * 2); // This does not include the label + var sortSelectWidth = + selectableWidth + sortByTextWidth + + style.ItemInnerSpacing.X; // Item spacing between the selectable and the label var headerText = Locs.Header_Hint; var headerTextSize = ImGui.CalcTextSize(headerText); @@ -433,7 +443,8 @@ internal class PluginInstallerWindow : Window, IDisposable var downShift = ImGui.GetCursorPosY() + (headerTextSize.Y / 4) - 2; ImGui.SetCursorPosY(downShift); - ImGui.SetCursorPosX(windowSize.X - sortSelectWidth - (style.ItemSpacing.X * 2) - searchInputWidth - searchClearButtonWidth); + ImGui.SetCursorPosX(windowSize.X - sortSelectWidth - (style.ItemSpacing.X * 2) - searchInputWidth - + searchClearButtonWidth); var searchTextChanged = false; ImGui.SetNextItemWidth(searchInputWidth); @@ -537,7 +548,8 @@ internal class PluginInstallerWindow : Window, IDisposable { ImGuiComponents.DisabledButton(Locs.FooterButton_UpdateSafeMode); } - else if (!ready || this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress) + else if (!ready || this.updateStatus == OperationStatus.InProgress || + this.installStatus == OperationStatus.InProgress) { ImGuiComponents.DisabledButton(Locs.FooterButton_UpdatePlugins); } @@ -576,11 +588,14 @@ internal class PluginInstallerWindow : Window, IDisposable if (errorPluginCount > 0) { var errorMessage = this.updatePluginCount > 0 - ? Locs.ErrorModal_UpdaterFailPartial(this.updatePluginCount, errorPluginCount) + ? Locs.ErrorModal_UpdaterFailPartial( + this.updatePluginCount, errorPluginCount) : Locs.ErrorModal_UpdaterFail(errorPluginCount); var hintInsert = errorPlugins - .Aggregate(string.Empty, (current, pluginUpdateStatus) => $"{current}* {pluginUpdateStatus.InternalName}\n") + .Aggregate(string.Empty, + (current, pluginUpdateStatus) => + $"{current}* {pluginUpdateStatus.InternalName}\n") .TrimEnd(); errorMessage += Locs.ErrorModal_HintBlame(hintInsert); @@ -589,8 +604,12 @@ internal class PluginInstallerWindow : Window, IDisposable if (this.updatePluginCount > 0) { - Service.Get().PrintUpdatedPlugins(this.updatedPlugins, Locs.PluginUpdateHeader_Chatbox); - notifications.AddNotification(Locs.Notifications_UpdatesInstalled(this.updatePluginCount), Locs.Notifications_UpdatesInstalledTitle, NotificationType.Success); + Service.Get() + .PrintUpdatedPlugins( + this.updatedPlugins, Locs.PluginUpdateHeader_Chatbox); + notifications.AddNotification( + Locs.Notifications_UpdatesInstalled(this.updatePluginCount), + Locs.Notifications_UpdatesInstalledTitle, NotificationType.Success); var installedGroupIdx = this.categoryManager.GroupList.TakeWhile( x => x.GroupKind != PluginCategoryManager.GroupKind.Installed).Count(); @@ -598,7 +617,9 @@ internal class PluginInstallerWindow : Window, IDisposable } else if (this.updatePluginCount == 0) { - notifications.AddNotification(Locs.Notifications_NoUpdatesFound, Locs.Notifications_NoUpdatesFoundTitle, NotificationType.Info); + notifications.AddNotification(Locs.Notifications_NoUpdatesFound, + Locs.Notifications_NoUpdatesFoundTitle, + NotificationType.Info); } } }); @@ -610,7 +631,8 @@ internal class PluginInstallerWindow : Window, IDisposable { var modalTitle = Locs.ErrorModal_Title; - if (ImGui.BeginPopupModal(modalTitle, ref this.errorModalDrawing, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar)) + if (ImGui.BeginPopupModal(modalTitle, ref this.errorModalDrawing, + ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar)) { ImGui.Text(this.errorModalMessage); ImGui.Spacing(); @@ -646,13 +668,15 @@ internal class PluginInstallerWindow : Window, IDisposable if (this.updateModalPlugin == null) return; - if (ImGui.BeginPopupModal(modalTitle, ref this.updateModalDrawing, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar)) + if (ImGui.BeginPopupModal(modalTitle, ref this.updateModalDrawing, + ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar)) { ImGui.Text(Locs.UpdateModal_UpdateAvailable(this.updateModalPlugin.Name)); ImGui.Spacing(); var buttonWidth = 120f; - ImGui.SetCursorPosX((ImGui.GetWindowWidth() - ((buttonWidth * 2) - (ImGui.GetStyle().ItemSpacing.Y * 2))) / 2); + ImGui.SetCursorPosX((ImGui.GetWindowWidth() - ((buttonWidth * 2) - (ImGui.GetStyle().ItemSpacing.Y * 2))) / + 2); if (ImGui.Button(Locs.UpdateModal_Yes, new Vector2(buttonWidth, 40))) { @@ -687,7 +711,8 @@ internal class PluginInstallerWindow : Window, IDisposable { var modalTitle = Locs.TestingWarningModal_Title; - if (ImGui.BeginPopupModal(modalTitle, ref this.testingWarningModalDrawing, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar)) + if (ImGui.BeginPopupModal(modalTitle, ref this.testingWarningModalDrawing, + ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar)) { ImGui.Text(Locs.TestingWarningModal_DowngradeBody); @@ -720,7 +745,8 @@ internal class PluginInstallerWindow : Window, IDisposable { var modalTitle = Locs.FeedbackModal_Title; - if (ImGui.BeginPopupModal(modalTitle, ref this.feedbackModalDrawing, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar)) + if (ImGui.BeginPopupModal(modalTitle, ref this.feedbackModalDrawing, + ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoScrollbar)) { ImGui.TextUnformatted(Locs.FeedbackModal_Text(this.feedbackPlugin.Name)); @@ -890,7 +916,8 @@ internal class PluginInstallerWindow : Window, IDisposable { this.dalamudChangelogRefreshTaskCts = new CancellationTokenSource(); this.dalamudChangelogRefreshTask = - Task.Run(this.dalamudChangelogManager.ReloadChangelogAsync, this.dalamudChangelogRefreshTaskCts.Token); + Task.Run(this.dalamudChangelogManager.ReloadChangelogAsync, + this.dalamudChangelogRefreshTaskCts.Token); } return; @@ -910,8 +937,10 @@ internal class PluginInstallerWindow : Window, IDisposable changelogs = this.dalamudChangelogManager.Changelogs.OfType(); } - var sortedChangelogs = changelogs?.Where(x => this.searchText.IsNullOrWhitespace() || x.Title.ToLowerInvariant().Contains(this.searchText.ToLowerInvariant())) - .OrderByDescending(x => x.Date).ToList(); + var sortedChangelogs = changelogs?.Where(x => this.searchText.IsNullOrWhitespace() || + x.Title.ToLowerInvariant() + .Contains(this.searchText.ToLowerInvariant())) + .OrderByDescending(x => x.Date).ToList(); if (sortedChangelogs == null || !sortedChangelogs.Any()) { @@ -1041,12 +1070,16 @@ internal class PluginInstallerWindow : Window, IDisposable var useContentWidth = ImGui.GetContentRegionAvail().X; - if (ImGui.BeginChild("InstallerCategories", new Vector2(useContentWidth, useContentHeight * ImGuiHelpers.GlobalScale))) + if (ImGui.BeginChild("InstallerCategories", + new Vector2(useContentWidth, useContentHeight * ImGuiHelpers.GlobalScale))) { ImGui.PushStyleVar(ImGuiStyleVar.CellPadding, ImGuiHelpers.ScaledVector2(5, 0)); - if (ImGui.BeginTable("##InstallerCategoriesCont", 2, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersInnerV)) + if (ImGui.BeginTable("##InstallerCategoriesCont", 2, + ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Resizable | + ImGuiTableFlags.BordersInnerV)) { - ImGui.TableSetupColumn("##InstallerCategoriesSelector", ImGuiTableColumnFlags.WidthFixed, useMenuWidth * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("##InstallerCategoriesSelector", ImGuiTableColumnFlags.WidthFixed, + useMenuWidth * ImGuiHelpers.GlobalScale); ImGui.TableSetupColumn("##InstallerCategoriesBody", ImGuiTableColumnFlags.WidthStretch); ImGui.TableNextRow(); @@ -1090,7 +1123,10 @@ internal class PluginInstallerWindow : Window, IDisposable } ImGui.SetNextItemOpen(groupIdx == this.categoryManager.CurrentGroupIdx); - if (ImGui.CollapsingHeader(groupInfo.Name, groupIdx == this.categoryManager.CurrentGroupIdx ? ImGuiTreeNodeFlags.OpenOnDoubleClick : ImGuiTreeNodeFlags.None)) + if (ImGui.CollapsingHeader(groupInfo.Name, + groupIdx == this.categoryManager.CurrentGroupIdx + ? ImGuiTreeNodeFlags.OpenOnDoubleClick + : ImGuiTreeNodeFlags.None)) { if (this.categoryManager.CurrentGroupIdx != groupIdx) { @@ -1098,10 +1134,12 @@ internal class PluginInstallerWindow : Window, IDisposable } ImGui.Indent(); - var categoryItemSize = new Vector2(ImGui.GetContentRegionAvail().X - (5 * ImGuiHelpers.GlobalScale), ImGui.GetTextLineHeight()); + var categoryItemSize = new Vector2(ImGui.GetContentRegionAvail().X - (5 * ImGuiHelpers.GlobalScale), + ImGui.GetTextLineHeight()); for (var categoryIdx = 0; categoryIdx < groupInfo.Categories.Count; categoryIdx++) { - var categoryInfo = Array.Find(this.categoryManager.CategoryList, x => x.CategoryId == groupInfo.Categories[categoryIdx]); + var categoryInfo = Array.Find(this.categoryManager.CategoryList, + x => x.CategoryId == groupInfo.Categories[categoryIdx]); switch (categoryInfo.Condition) { @@ -1122,7 +1160,8 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PushStyleColor(ImGuiCol.Text, colorSearchHighlight); } - if (ImGui.Selectable(categoryInfo.Name, this.categoryManager.CurrentCategoryIdx == categoryIdx, ImGuiSelectableFlags.None, categoryItemSize)) + if (ImGui.Selectable(categoryInfo.Name, this.categoryManager.CurrentCategoryIdx == categoryIdx, + ImGuiSelectableFlags.None, categoryItemSize)) { this.categoryManager.CurrentCategoryIdx = categoryIdx; } @@ -1298,7 +1337,8 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0.5f, 0.5f, 0.5f, 0.35f)); ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0); - ImGui.Button($"###pluginTesterCollapsibleBtn", new Vector2(ImGui.GetWindowWidth() - (ImGuiHelpers.GlobalScale * 35), sectionSize)); + ImGui.Button($"###pluginTesterCollapsibleBtn", + new Vector2(ImGui.GetWindowWidth() - (ImGuiHelpers.GlobalScale * 35), sectionSize)); ImGui.PopStyleVar(); @@ -1362,7 +1402,8 @@ internal class PluginInstallerWindow : Window, IDisposable ImGuiHelpers.ScaledDummy(5); // Controls - var disabled = this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress; + var disabled = this.updateStatus == OperationStatus.InProgress || + this.installStatus == OperationStatus.InProgress; var versionString = "1.0.0.0"; @@ -1392,7 +1433,8 @@ internal class PluginInstallerWindow : Window, IDisposable if (ImGui.BeginChild( "pluginTestingImageScrolling", - new Vector2(width - (70 * ImGuiHelpers.GlobalScale), (PluginImageCache.PluginImageHeight / thumbFactor) + scrollBarSize), + new Vector2(width - (70 * ImGuiHelpers.GlobalScale), + (PluginImageCache.PluginImageHeight / thumbFactor) + scrollBarSize), false, ImGuiWindowFlags.HorizontalScrollbar | ImGuiWindowFlags.NoScrollWithMouse | @@ -1467,9 +1509,11 @@ internal class PluginInstallerWindow : Window, IDisposable if (image == null) return; if (image.Width > maxWidth || image.Height > maxHeight) - ImGui.TextColored(ImGuiColors.DalamudRed, $"Image is larger than the maximum allowed resolution ({image.Width}x{image.Height} > {maxWidth}x{maxHeight})"); + ImGui.TextColored(ImGuiColors.DalamudRed, + $"Image is larger than the maximum allowed resolution ({image.Width}x{image.Height} > {maxWidth}x{maxHeight})"); if (requireSquare && image.Width != image.Height) - ImGui.TextColored(ImGuiColors.DalamudRed, $"Image must be square! Current size: {image.Width}x{image.Height}"); + ImGui.TextColored(ImGuiColors.DalamudRed, + $"Image must be square! Current size: {image.Width}x{image.Height}"); } ImGui.InputText("Icon Path", ref this.testerIconPath, 1000); @@ -1477,19 +1521,24 @@ internal class PluginInstallerWindow : Window, IDisposable CheckImageSize(this.testerIcon, PluginImageCache.PluginIconWidth, PluginImageCache.PluginIconHeight, true); ImGui.InputText("Image 1 Path", ref this.testerImagePaths[0], 1000); if (this.testerImages?.Length > 0) - CheckImageSize(this.testerImages[0], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, false); + CheckImageSize(this.testerImages[0], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, + false); ImGui.InputText("Image 2 Path", ref this.testerImagePaths[1], 1000); if (this.testerImages?.Length > 1) - CheckImageSize(this.testerImages[1], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, false); + CheckImageSize(this.testerImages[1], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, + false); ImGui.InputText("Image 3 Path", ref this.testerImagePaths[2], 1000); if (this.testerImages?.Length > 2) - CheckImageSize(this.testerImages[2], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, false); + CheckImageSize(this.testerImages[2], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, + false); ImGui.InputText("Image 4 Path", ref this.testerImagePaths[3], 1000); if (this.testerImages?.Length > 3) - CheckImageSize(this.testerImages[3], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, false); + CheckImageSize(this.testerImages[3], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, + false); ImGui.InputText("Image 5 Path", ref this.testerImagePaths[4], 1000); if (this.testerImages?.Length > 4) - CheckImageSize(this.testerImages[4], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, false); + CheckImageSize(this.testerImages[4], PluginImageCache.PluginImageWidth, PluginImageCache.PluginImageHeight, + false); var im = Service.Get(); if (ImGui.Button("Load")) @@ -1561,7 +1610,10 @@ internal class PluginInstallerWindow : Window, IDisposable return ready; } - private bool DrawPluginCollapsingHeader(string label, LocalPlugin? plugin, PluginManifest manifest, bool isThirdParty, bool trouble, bool updateAvailable, bool isNew, bool installableOutdated, bool isOrphan, Action drawContextMenuAction, int index) + private bool DrawPluginCollapsingHeader( + string label, LocalPlugin? plugin, PluginManifest manifest, bool isThirdParty, bool trouble, + bool updateAvailable, bool isNew, bool installableOutdated, bool isOrphan, Action drawContextMenuAction, + int index) { ImGui.Separator(); @@ -1576,7 +1628,8 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(0.5f, 0.5f, 0.5f, 0.35f)); ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0); - if (ImGui.Button($"###plugin{index}CollapsibleBtn", new Vector2(ImGui.GetWindowWidth() - (ImGuiHelpers.GlobalScale * 35), sectionSize))) + if (ImGui.Button($"###plugin{index}CollapsibleBtn", + new Vector2(ImGui.GetWindowWidth() - (ImGuiHelpers.GlobalScale * 35), sectionSize))) { if (isOpen) { @@ -1763,7 +1816,8 @@ internal class PluginInstallerWindow : Window, IDisposable if (log is PluginChangelogEntry pluginLog) { icon = this.imageCache.DefaultIcon; - var hasIcon = this.imageCache.TryGetIcon(pluginLog.Plugin, pluginLog.Plugin.Manifest, pluginLog.Plugin.Manifest.IsThirdParty, out var cachedIconTex); + var hasIcon = this.imageCache.TryGetIcon(pluginLog.Plugin, pluginLog.Plugin.Manifest, + pluginLog.Plugin.Manifest.IsThirdParty, out var cachedIconTex); if (hasIcon && cachedIconTex != null) { icon = cachedIconTex; @@ -1850,7 +1904,8 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PushID($"available{index}{manifest.InternalName}"); var isThirdParty = manifest.SourceRepo.IsThirdParty; - if (this.DrawPluginCollapsingHeader(label, null, manifest, isThirdParty, false, false, !wasSeen, isOutdated, false, () => this.DrawAvailablePluginContextMenu(manifest), index)) + if (this.DrawPluginCollapsingHeader(label, null, manifest, isThirdParty, false, false, !wasSeen, isOutdated, + false, () => this.DrawAvailablePluginContextMenu(manifest), index)) { if (!wasSeen) configuration.SeenPluginInternalName.Add(manifest.InternalName); @@ -1877,7 +1932,8 @@ internal class PluginInstallerWindow : Window, IDisposable ImGuiHelpers.ScaledDummy(5); // Controls - var disabled = this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress || isOutdated; + var disabled = this.updateStatus == OperationStatus.InProgress || + this.installStatus == OperationStatus.InProgress || isOutdated; var versionString = useTesting ? $"{manifest.TestingAssemblyVersion}" @@ -1899,7 +1955,8 @@ internal class PluginInstallerWindow : Window, IDisposable this.installStatus = OperationStatus.InProgress; this.loadingIndicatorKind = LoadingIndicatorKind.Installing; - Task.Run(() => pluginManager.InstallPluginAsync(manifest, useTesting || manifest.IsTestingExclusive, PluginLoadReason.Installer)) + Task.Run(() => pluginManager.InstallPluginAsync(manifest, useTesting || manifest.IsTestingExclusive, + PluginLoadReason.Installer)) .ContinueWith(task => { // There is no need to set as Complete for an individual plugin installation @@ -1908,11 +1965,15 @@ internal class PluginInstallerWindow : Window, IDisposable { if (task.Result.State == PluginState.Loaded) { - notifications.AddNotification(Locs.Notifications_PluginInstalled(manifest.Name), Locs.Notifications_PluginInstalledTitle, NotificationType.Success); + notifications.AddNotification(Locs.Notifications_PluginInstalled(manifest.Name), + Locs.Notifications_PluginInstalledTitle, + NotificationType.Success); } else { - notifications.AddNotification(Locs.Notifications_PluginNotInstalled(manifest.Name), Locs.Notifications_PluginNotInstalledTitle, NotificationType.Error); + notifications.AddNotification(Locs.Notifications_PluginNotInstalled(manifest.Name), + Locs.Notifications_PluginNotInstalledTitle, + NotificationType.Error); this.ShowErrorModal(Locs.ErrorModal_InstallFail(manifest.Name)); } } @@ -2055,7 +2116,8 @@ internal class PluginInstallerWindow : Window, IDisposable var thisWasUpdated = false; if (this.updatedPlugins != null && !plugin.IsDev) { - var update = this.updatedPlugins.FirstOrDefault(update => update.InternalName == plugin.Manifest.InternalName); + var update = + this.updatedPlugins.FirstOrDefault(update => update.InternalName == plugin.Manifest.InternalName); if (update != default) { if (update.WasUpdated) @@ -2107,7 +2169,9 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PushID($"installed{index}{plugin.Manifest.InternalName}"); var hasChangelog = !plugin.Manifest.Changelog.IsNullOrEmpty(); - if (this.DrawPluginCollapsingHeader(label, plugin, plugin.Manifest, plugin.Manifest.IsThirdParty, trouble, availablePluginUpdate != default, false, false, plugin.IsOrphaned, () => this.DrawInstalledPluginContextMenu(plugin, testingOptIn), index)) + if (this.DrawPluginCollapsingHeader(label, plugin, plugin.Manifest, plugin.Manifest.IsThirdParty, trouble, + availablePluginUpdate != default, false, false, plugin.IsOrphaned, + () => this.DrawInstalledPluginContextMenu(plugin, testingOptIn), index)) { if (!this.WasPluginSeen(plugin.Manifest.InternalName)) configuration.SeenPluginInternalName.Add(plugin.Manifest.InternalName); @@ -2123,14 +2187,17 @@ internal class PluginInstallerWindow : Window, IDisposable var downloadText = plugin.IsDev ? Locs.PluginBody_AuthorWithoutDownloadCount(manifest.Author) : manifest.DownloadCount > 0 - ? Locs.PluginBody_AuthorWithDownloadCount(manifest.Author, manifest.DownloadCount) + ? Locs.PluginBody_AuthorWithDownloadCount( + manifest.Author, manifest.DownloadCount) : Locs.PluginBody_AuthorWithDownloadCountUnavailable(manifest.Author); ImGui.SameLine(); ImGui.TextColored(ImGuiColors.DalamudGrey3, downloadText); var isThirdParty = manifest.IsThirdParty; - var canFeedback = !isThirdParty && !plugin.IsDev && plugin.Manifest.DalamudApiLevel == PluginManager.DalamudApiLevel && plugin.Manifest.AcceptsFeedback && availablePluginUpdate == default; + var canFeedback = !isThirdParty && !plugin.IsDev && + plugin.Manifest.DalamudApiLevel == PluginManager.DalamudApiLevel && + plugin.Manifest.AcceptsFeedback && availablePluginUpdate == default; // Installed from if (plugin.IsDev) @@ -2154,7 +2221,8 @@ internal class PluginInstallerWindow : Window, IDisposable if (plugin.IsLoaded) { var commands = commandManager.Commands - .Where(cInfo => cInfo.Value.ShowInHelp && cInfo.Value.LoaderAssemblyName == plugin.Manifest.InternalName) + .Where(cInfo => cInfo.Value.ShowInHelp && cInfo.Value.LoaderAssemblyName == + plugin.Manifest.InternalName) .ToArray(); if (commands.Any()) @@ -2200,9 +2268,12 @@ internal class PluginInstallerWindow : Window, IDisposable } } - if (availablePluginUpdate != default && !availablePluginUpdate.UpdateManifest.Changelog.IsNullOrWhitespace()) + if (availablePluginUpdate != default && + !availablePluginUpdate.UpdateManifest.Changelog.IsNullOrWhitespace()) { - var availablePluginUpdateVersion = availablePluginUpdate.UseTesting ? availablePluginUpdate.UpdateManifest.TestingAssemblyVersion : availablePluginUpdate.UpdateManifest.AssemblyVersion; + var availablePluginUpdateVersion = availablePluginUpdate.UseTesting + ? availablePluginUpdate.UpdateManifest.TestingAssemblyVersion + : availablePluginUpdate.UpdateManifest.AssemblyVersion; if (ImGui.TreeNode(Locs.PluginBody_UpdateChangeLog(availablePluginUpdateVersion))) { this.DrawInstalledPluginChangelog(availablePluginUpdate.UpdateManifest); @@ -2228,7 +2299,9 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(7, 5)); - if (ImGui.BeginChild("##changelog", new Vector2(-1, 100), true, ImGuiWindowFlags.NoNavFocus | ImGuiWindowFlags.NoNavInputs | ImGuiWindowFlags.AlwaysAutoResize)) + if (ImGui.BeginChild("##changelog", new Vector2(-1, 100), true, + ImGuiWindowFlags.NoNavFocus | ImGuiWindowFlags.NoNavInputs | + ImGuiWindowFlags.AlwaysAutoResize)) { ImGui.Text("Changelog:"); ImGuiHelpers.ScaledDummy(2); @@ -2250,7 +2323,8 @@ internal class PluginInstallerWindow : Window, IDisposable { if (configuration.DoPluginTest) { - var repoManifest = this.pluginListAvailable.FirstOrDefault(x => x.InternalName == plugin.Manifest.InternalName); + var repoManifest = + this.pluginListAvailable.FirstOrDefault(x => x.InternalName == plugin.Manifest.InternalName); if (repoManifest?.IsTestingExclusive == true) ImGui.BeginDisabled(); @@ -2302,7 +2376,8 @@ internal class PluginInstallerWindow : Window, IDisposable var pluginManager = Service.Get(); // Disable everything if the updater is running or another plugin is operating - var disabled = this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress; + var disabled = this.updateStatus == OperationStatus.InProgress || + this.installStatus == OperationStatus.InProgress; // Disable everything if the plugin is outdated disabled = disabled || (plugin.IsOutdated && !pluginManager.LoadAllApiLevels) || plugin.IsBanned; @@ -2312,7 +2387,8 @@ internal class PluginInstallerWindow : Window, IDisposable disabled = disabled || (plugin.IsOrphaned && !plugin.IsLoaded); // Disable everything if the plugin failed to load - disabled = disabled || plugin.State == PluginState.LoadError || plugin.State == PluginState.DependencyResolutionFailed; + disabled = disabled || plugin.State == PluginState.LoadError || + plugin.State == PluginState.DependencyResolutionFailed; // Disable everything if we're working disabled = disabled || plugin.State == PluginState.Loading || plugin.State == PluginState.Unloading; @@ -2351,7 +2427,8 @@ internal class PluginInstallerWindow : Window, IDisposable } var unloadTask = Task.Run(() => plugin.UnloadAsync()) - .ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_UnloadFail(plugin.Name)); + .ContinueWith(this.DisplayErrorContinuation, + Locs.ErrorModal_UnloadFail(plugin.Name)); unloadTask.Wait(); if (!unloadTask.Result) @@ -2361,7 +2438,8 @@ internal class PluginInstallerWindow : Window, IDisposable } var disableTask = Task.Run(() => plugin.Disable()) - .ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_DisableFail(plugin.Name)); + .ContinueWith(this.DisplayErrorContinuation, + Locs.ErrorModal_DisableFail(plugin.Name)); disableTask.Wait(); this.enableDisableStatus = OperationStatus.Complete; @@ -2369,7 +2447,8 @@ internal class PluginInstallerWindow : Window, IDisposable if (!disableTask.Result) return; - notifications.AddNotification(Locs.Notifications_PluginDisabled(plugin.Manifest.Name), Locs.Notifications_PluginDisabledTitle, NotificationType.Success); + notifications.AddNotification(Locs.Notifications_PluginDisabled(plugin.Manifest.Name), + Locs.Notifications_PluginDisabledTitle, NotificationType.Success); }); } else @@ -2587,7 +2666,8 @@ internal class PluginInstallerWindow : Window, IDisposable var pluginManager = Service.Get(); - var devNotDeletable = plugin.IsDev && plugin.State != PluginState.Unloaded && plugin.State != PluginState.DependencyResolutionFailed; + var devNotDeletable = plugin.IsDev && plugin.State != PluginState.Unloaded && + plugin.State != PluginState.DependencyResolutionFailed; ImGui.SameLine(); if (plugin.State == PluginState.Loaded || devNotDeletable) @@ -2692,7 +2772,11 @@ internal class PluginInstallerWindow : Window, IDisposable var width = ImGui.GetWindowWidth(); - if (ImGui.BeginChild($"plugin{index}ImageScrolling", new Vector2(width - (70 * ImGuiHelpers.GlobalScale), (PluginImageCache.PluginImageHeight / thumbFactor) + scrollBarSize), false, ImGuiWindowFlags.HorizontalScrollbar | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoBackground)) + if (ImGui.BeginChild($"plugin{index}ImageScrolling", + new Vector2(width - (70 * ImGuiHelpers.GlobalScale), + (PluginImageCache.PluginImageHeight / thumbFactor) + scrollBarSize), false, + ImGuiWindowFlags.HorizontalScrollbar | ImGuiWindowFlags.NoScrollWithMouse | + ImGuiWindowFlags.NoBackground)) { for (var i = 0; i < imageTextures.Length; i++) { @@ -2766,16 +2850,22 @@ internal class PluginInstallerWindow : Window, IDisposable return hasSearchString && !( manifest.Name.ToLowerInvariant().Contains(searchString) || manifest.InternalName.ToLowerInvariant().Contains(searchString) || - (!manifest.Author.IsNullOrEmpty() && manifest.Author.Equals(this.searchText, StringComparison.InvariantCultureIgnoreCase)) || - (!manifest.Punchline.IsNullOrEmpty() && manifest.Punchline.ToLowerInvariant().Contains(searchString)) || - (manifest.Tags != null && manifest.Tags.Contains(searchString, StringComparer.InvariantCultureIgnoreCase))); + (!manifest.Author.IsNullOrEmpty() && + manifest.Author.Equals(this.searchText, + StringComparison.InvariantCultureIgnoreCase)) || + (!manifest.Punchline.IsNullOrEmpty() && manifest.Punchline.ToLowerInvariant() + .Contains(searchString)) || + (manifest.Tags != null && + manifest.Tags.Contains(searchString, + StringComparer.InvariantCultureIgnoreCase))); } private (bool IsInstalled, LocalPlugin Plugin) IsManifestInstalled(PluginManifest? manifest) { if (manifest == null) return (false, default); - var plugin = this.pluginListInstalled.FirstOrDefault(plugin => plugin.Manifest.InternalName == manifest.InternalName); + var plugin = + this.pluginListInstalled.FirstOrDefault(plugin => plugin.Manifest.InternalName == manifest.InternalName); var isInstalled = plugin != default; return (isInstalled, plugin); @@ -2822,7 +2912,8 @@ internal class PluginInstallerWindow : Window, IDisposable break; case PluginSortKind.DownloadCount: this.pluginListAvailable.Sort((p1, p2) => p2.DownloadCount.CompareTo(p1.DownloadCount)); - this.pluginListInstalled.Sort((p1, p2) => p2.Manifest.DownloadCount.CompareTo(p1.Manifest.DownloadCount)); + this.pluginListInstalled.Sort( + (p1, p2) => p2.Manifest.DownloadCount.CompareTo(p1.Manifest.DownloadCount)); break; case PluginSortKind.LastUpdate: this.pluginListAvailable.Sort((p1, p2) => p2.LastUpdate.CompareTo(p1.LastUpdate)); @@ -2835,9 +2926,14 @@ internal class PluginInstallerWindow : Window, IDisposable .CompareTo(this.WasPluginSeen(p2.Manifest.InternalName))); break; case PluginSortKind.NotInstalled: - this.pluginListAvailable.Sort((p1, p2) => this.pluginListInstalled.Any(x => x.Manifest.InternalName == p1.InternalName) - .CompareTo(this.pluginListInstalled.Any(x => x.Manifest.InternalName == p2.InternalName))); - this.pluginListInstalled.Sort((p1, p2) => p1.Manifest.Name.CompareTo(p2.Manifest.Name)); // Makes no sense for installed plugins + this.pluginListAvailable.Sort((p1, p2) => this + .pluginListInstalled + .Any(x => x.Manifest.InternalName == p1.InternalName) + .CompareTo(this.pluginListInstalled.Any( + x => x.Manifest.InternalName == + p2.InternalName))); + this.pluginListInstalled.Sort( + (p1, p2) => p1.Manifest.Name.CompareTo(p2.Manifest.Name)); // Makes no sense for installed plugins break; case PluginSortKind.EnabledDisabled: this.pluginListAvailable.Sort((p1, p2) => @@ -2849,7 +2945,8 @@ internal class PluginInstallerWindow : Window, IDisposable return IsEnabled(p2).CompareTo(IsEnabled(p1)); }); - this.pluginListInstalled.Sort((p1, p2) => (p2.State == PluginState.Loaded).CompareTo(p1.State == PluginState.Loaded)); + this.pluginListInstalled.Sort( + (p1, p2) => (p2.State == PluginState.Loaded).CompareTo(p1.State == PluginState.Loaded)); break; default: throw new InvalidEnumArgumentException("Unknown plugin sort type."); @@ -2935,8 +3032,10 @@ internal class PluginInstallerWindow : Window, IDisposable this.UpdateCategoriesOnSearchChange(); } - [SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Disregard here")] - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Locs")] + [SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", + Justification = "Disregard here")] + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", + Justification = "Locs")] internal static class Locs { #region Window Title @@ -2949,7 +3048,8 @@ internal class PluginInstallerWindow : Window, IDisposable #region Header - public static string Header_Hint => Loc.Localize("InstallerHint", "This window allows you to install and remove in-game plugins.\nThey are made by third-party developers."); + public static string Header_Hint => Loc.Localize("InstallerHint", + "This window allows you to install and remove in-game plugins.\nThey are made by third-party developers."); public static string Header_SearchPlaceholder => Loc.Localize("InstallerSearch", "Search"); @@ -2979,21 +3079,27 @@ internal class PluginInstallerWindow : Window, IDisposable public static string TabBody_DownloadFailed => Loc.Localize("InstallerDownloadFailed", "Download failed."); - public static string TabBody_SafeMode => Loc.Localize("InstallerSafeMode", "Dalamud is running in Plugin Safe Mode, restart to activate plugins."); + public static string TabBody_SafeMode => Loc.Localize("InstallerSafeMode", + "Dalamud is running in Plugin Safe Mode, restart to activate plugins."); #endregion #region Search text - public static string TabBody_SearchNoMatching => Loc.Localize("InstallerNoMatching", "No plugins were found matching your search."); + public static string TabBody_SearchNoMatching => + Loc.Localize("InstallerNoMatching", "No plugins were found matching your search."); - public static string TabBody_SearchNoCompatible => Loc.Localize("InstallerNoCompatible", "No compatible plugins were found :( Please restart your game and try again."); + public static string TabBody_SearchNoCompatible => Loc.Localize( + "InstallerNoCompatible", "No compatible plugins were found :( Please restart your game and try again."); - public static string TabBody_SearchNoInstalled => Loc.Localize("InstallerNoInstalled", "No plugins are currently installed. You can install them from the \"All Plugins\" tab."); + public static string TabBody_SearchNoInstalled => Loc.Localize("InstallerNoInstalled", + "No plugins are currently installed. You can install them from the \"All Plugins\" tab."); - public static string TabBody_ChangelogNone => Loc.Localize("InstallerNoChangelog", "None of your installed plugins have a changelog."); + public static string TabBody_ChangelogNone => + Loc.Localize("InstallerNoChangelog", "None of your installed plugins have a changelog."); - public static string TabBody_ChangelogError => Loc.Localize("InstallerChangelogError", "Could not download changelogs."); + public static string TabBody_ChangelogError => + Loc.Localize("InstallerChangelogError", "Could not download changelogs."); #endregion @@ -3011,11 +3117,14 @@ internal class PluginInstallerWindow : Window, IDisposable public static string PluginTitleMod_Updated => Loc.Localize("InstallerUpdated", " (updated)"); - public static string PluginTitleMod_TestingVersion => Loc.Localize("InstallerTestingVersion", " (testing version)"); + public static string PluginTitleMod_TestingVersion => + Loc.Localize("InstallerTestingVersion", " (testing version)"); - public static string PluginTitleMod_TestingExclusive => Loc.Localize("InstallerTestingExclusive", " (testing exclusive)"); + public static string PluginTitleMod_TestingExclusive => + Loc.Localize("InstallerTestingExclusive", " (testing exclusive)"); - public static string PluginTitleMod_TestingAvailable => Loc.Localize("InstallerTestingAvailable", " (has testing version)"); + public static string PluginTitleMod_TestingAvailable => + Loc.Localize("InstallerTestingAvailable", " (has testing version)"); public static string PluginTitleMod_DevPlugin => Loc.Localize("InstallerDevPlugin", " (dev plugin)"); @@ -3027,11 +3136,14 @@ internal class PluginInstallerWindow : Window, IDisposable public static string PluginTitleMod_OutdatedError => Loc.Localize("InstallerOutdatedError", " (outdated)"); - public static string PluginTitleMod_BannedError => Loc.Localize("InstallerBannedError", " (automatically disabled)"); + public static string PluginTitleMod_BannedError => + Loc.Localize("InstallerBannedError", " (automatically disabled)"); - public static string PluginTitleMod_OrphanedError => Loc.Localize("InstallerOrphanedError", " (unknown repository)"); + public static string PluginTitleMod_OrphanedError => + Loc.Localize("InstallerOrphanedError", " (unknown repository)"); - public static string PluginTitleMod_ScheduledForDeletion => Loc.Localize("InstallerScheduledForDeletion", " (scheduled for deletion)"); + public static string PluginTitleMod_ScheduledForDeletion => + Loc.Localize("InstallerScheduledForDeletion", " (scheduled for deletion)"); public static string PluginTitleMod_New => Loc.Localize("InstallerNewPlugin ", " New!"); @@ -3039,56 +3151,79 @@ internal class PluginInstallerWindow : Window, IDisposable #region Plugin context menu - public static string PluginContext_TestingOptIn => Loc.Localize("InstallerTestingOptIn", "Receive plugin testing versions"); + public static string PluginContext_TestingOptIn => + Loc.Localize("InstallerTestingOptIn", "Receive plugin testing versions"); public static string PluginContext_MarkAllSeen => Loc.Localize("InstallerMarkAllSeen", "Mark all as seen"); public static string PluginContext_HidePlugin => Loc.Localize("InstallerHidePlugin", "Hide from installer"); - public static string PluginContext_DeletePluginConfig => Loc.Localize("InstallerDeletePluginConfig", "Reset plugin configuration"); + public static string PluginContext_DeletePluginConfig => + Loc.Localize("InstallerDeletePluginConfig", "Reset plugin configuration"); - public static string PluginContext_DeletePluginConfigReload => Loc.Localize("InstallerDeletePluginConfigReload", "Reset plugin configuration and reload"); + public static string PluginContext_DeletePluginConfigReload => + Loc.Localize("InstallerDeletePluginConfigReload", "Reset plugin configuration and reload"); #endregion #region Plugin body - public static string PluginBody_AuthorWithoutDownloadCount(string author) => Loc.Localize("InstallerAuthorWithoutDownloadCount", " by {0}").Format(author); + public static string PluginBody_AuthorWithoutDownloadCount(string author) => + Loc.Localize("InstallerAuthorWithoutDownloadCount", " by {0}").Format(author); - public static string PluginBody_AuthorWithDownloadCount(string author, long count) => Loc.Localize("InstallerAuthorWithDownloadCount", " by {0} ({1} downloads)").Format(author, count.ToString("N0")); + public static string PluginBody_AuthorWithDownloadCount(string author, long count) => Loc + .Localize("InstallerAuthorWithDownloadCount", " by {0} ({1} downloads)") + .Format(author, count.ToString("N0")); - public static string PluginBody_AuthorWithDownloadCountUnavailable(string author) => Loc.Localize("InstallerAuthorWithDownloadCountUnavailable", " by {0}").Format(author); + public static string PluginBody_AuthorWithDownloadCountUnavailable(string author) => + Loc.Localize("InstallerAuthorWithDownloadCountUnavailable", " by {0}").Format(author); - public static string PluginBody_CurrentChangeLog(Version version) => Loc.Localize("InstallerCurrentChangeLog", "Changelog (v{0})").Format(version); + public static string PluginBody_CurrentChangeLog(Version version) => + Loc.Localize("InstallerCurrentChangeLog", "Changelog (v{0})").Format(version); - public static string PluginBody_UpdateChangeLog(Version version) => Loc.Localize("InstallerUpdateChangeLog", "Available update changelog (v{0})").Format(version); + public static string PluginBody_UpdateChangeLog(Version version) => Loc + .Localize("InstallerUpdateChangeLog", + "Available update changelog (v{0})") + .Format(version); - public static string PluginBody_DevPluginPath(string path) => Loc.Localize("InstallerDevPluginPath", "From {0}").Format(path); + public static string PluginBody_DevPluginPath(string path) => + Loc.Localize("InstallerDevPluginPath", "From {0}").Format(path); - public static string PluginBody_Plugin3rdPartyRepo(string url) => Loc.Localize("InstallerPlugin3rdPartyRepo", "From custom plugin repository {0}").Format(url); + public static string PluginBody_Plugin3rdPartyRepo(string url) => + Loc.Localize("InstallerPlugin3rdPartyRepo", "From custom plugin repository {0}").Format(url); - public static string PluginBody_Outdated => Loc.Localize("InstallerOutdatedPluginBody ", "This plugin is outdated and incompatible at the moment. Please wait for it to be updated by its author."); + public static string PluginBody_Outdated => Loc.Localize("InstallerOutdatedPluginBody ", + "This plugin is outdated and incompatible at the moment. Please wait for it to be updated by its author."); - public static string PluginBody_Orphaned => Loc.Localize("InstallerOrphanedPluginBody ", "This plugin's source repository is no longer available. You may need to reinstall it from its repository, or re-add the repository."); + public static string PluginBody_Orphaned => Loc.Localize("InstallerOrphanedPluginBody ", + "This plugin's source repository is no longer available. You may need to reinstall it from its repository, or re-add the repository."); - public static string PluginBody_NoServiceOfficial => Loc.Localize("InstallerNoServiceOfficialPluginBody", "This plugin is no longer being maintained. It will still work, but there will be no further updates and you can't reinstall it."); + public static string PluginBody_NoServiceOfficial => Loc.Localize( + "InstallerNoServiceOfficialPluginBody", + "This plugin is no longer being maintained. It will still work, but there will be no further updates and you can't reinstall it."); - public static string PluginBody_NoServiceThird => Loc.Localize("InstallerNoServiceThirdPluginBody", "This plugin is no longer being serviced by its source repo. You may have to look for an updated version in another repo."); + public static string PluginBody_NoServiceThird => Loc.Localize("InstallerNoServiceThirdPluginBody", + "This plugin is no longer being serviced by its source repo. You may have to look for an updated version in another repo."); - public static string PluginBody_LoadFailed => Loc.Localize("InstallerLoadFailedPluginBody ", "This plugin failed to load. Please contact the author for more information."); + public static string PluginBody_LoadFailed => Loc.Localize("InstallerLoadFailedPluginBody ", + "This plugin failed to load. Please contact the author for more information."); - public static string PluginBody_Banned => Loc.Localize("InstallerBannedPluginBody ", "This plugin was automatically disabled due to incompatibilities and is not available at the moment. Please wait for it to be updated by its author."); + public static string PluginBody_Banned => Loc.Localize("InstallerBannedPluginBody ", + "This plugin was automatically disabled due to incompatibilities and is not available at the moment. Please wait for it to be updated by its author."); - public static string PluginBody_Policy => Loc.Localize("InstallerPolicyPluginBody ", "Plugin loads for this type of plugin were manually disabled."); + public static string PluginBody_Policy => Loc.Localize("InstallerPolicyPluginBody ", + "Plugin loads for this type of plugin were manually disabled."); public static string PluginBody_BannedReason(string message) => - Loc.Localize("InstallerBannedPluginBodyReason ", "This plugin was automatically disabled: {0}").Format(message); + Loc.Localize("InstallerBannedPluginBodyReason ", "This plugin was automatically disabled: {0}") + .Format(message); #endregion #region Plugin buttons - public static string PluginButton_InstallVersion(string version) => Loc.Localize("InstallerInstall", "Install v{0}").Format(version); + public static string PluginButton_InstallVersion(string version) => + Loc.Localize("InstallerInstall", "Install v{0}").Format(version); public static string PluginButton_Working => Loc.Localize("InstallerWorking", "Working"); @@ -3098,61 +3233,88 @@ internal class PluginInstallerWindow : Window, IDisposable public static string PluginButton_Unload => Loc.Localize("InstallerUnload", "Unload"); - public static string PluginButton_SafeMode => Loc.Localize("InstallerSafeModeButton", "Can't change in safe mode"); + public static string PluginButton_SafeMode => + Loc.Localize("InstallerSafeModeButton", "Can't change in safe mode"); #endregion #region Plugin button tooltips - public static string PluginButtonToolTip_OpenConfiguration => Loc.Localize("InstallerOpenConfig", "Open Configuration"); + public static string PluginButtonToolTip_OpenConfiguration => + Loc.Localize("InstallerOpenConfig", "Open Configuration"); public static string PluginButtonToolTip_StartOnBoot => Loc.Localize("InstallerStartOnBoot", "Start on boot"); - public static string PluginButtonToolTip_AutomaticReloading => Loc.Localize("InstallerAutomaticReloading", "Automatic reloading"); + public static string PluginButtonToolTip_AutomaticReloading => + Loc.Localize("InstallerAutomaticReloading", "Automatic reloading"); - public static string PluginButtonToolTip_DeletePlugin => Loc.Localize("InstallerDeletePlugin ", "Delete plugin"); + public static string PluginButtonToolTip_DeletePlugin => + Loc.Localize("InstallerDeletePlugin ", "Delete plugin"); - public static string PluginButtonToolTip_DeletePluginRestricted => Loc.Localize("InstallerDeletePluginRestricted", "Cannot delete right now - please restart the game."); + public static string PluginButtonToolTip_DeletePluginRestricted => + Loc.Localize("InstallerDeletePluginRestricted", "Cannot delete right now - please restart the game."); - public static string PluginButtonToolTip_DeletePluginScheduled => Loc.Localize("InstallerDeletePluginScheduled", "Delete plugin on next restart"); + public static string PluginButtonToolTip_DeletePluginScheduled => + Loc.Localize("InstallerDeletePluginScheduled", "Delete plugin on next restart"); - public static string PluginButtonToolTip_DeletePluginScheduledCancel => Loc.Localize("InstallerDeletePluginScheduledCancel", "Cancel scheduled deletion"); + public static string PluginButtonToolTip_DeletePluginScheduledCancel => + Loc.Localize("InstallerDeletePluginScheduledCancel", "Cancel scheduled deletion"); - public static string PluginButtonToolTip_DeletePluginLoaded => Loc.Localize("InstallerDeletePluginLoaded", "Disable this plugin before deleting it."); + public static string PluginButtonToolTip_DeletePluginLoaded => + Loc.Localize("InstallerDeletePluginLoaded", "Disable this plugin before deleting it."); - public static string PluginButtonToolTip_VisitPluginUrl => Loc.Localize("InstallerVisitPluginUrl", "Visit plugin URL"); + public static string PluginButtonToolTip_VisitPluginUrl => + Loc.Localize("InstallerVisitPluginUrl", "Visit plugin URL"); - public static string PluginButtonToolTip_UpdateSingle(string version) => Loc.Localize("InstallerUpdateSingle", "Update to {0}").Format(version); + public static string PluginButtonToolTip_UpdateSingle(string version) => + Loc.Localize("InstallerUpdateSingle", "Update to {0}").Format(version); - public static string PluginButtonToolTip_UnloadFailed => Loc.Localize("InstallerUnloadFailedTooltip", "Plugin unload failed, please restart your game and try again."); + public static string PluginButtonToolTip_UnloadFailed => Loc.Localize( + "InstallerUnloadFailedTooltip", "Plugin unload failed, please restart your game and try again."); #endregion #region Notifications - public static string Notifications_PluginInstalledTitle => Loc.Localize("NotificationsPluginInstalledTitle", "Plugin installed!"); + public static string Notifications_PluginInstalledTitle => + Loc.Localize("NotificationsPluginInstalledTitle", "Plugin installed!"); - public static string Notifications_PluginInstalled(string name) => Loc.Localize("NotificationsPluginInstalled", "'{0}' was successfully installed.").Format(name); + public static string Notifications_PluginInstalled(string name) => Loc + .Localize("NotificationsPluginInstalled", + "'{0}' was successfully installed.") + .Format(name); - public static string Notifications_PluginNotInstalledTitle => Loc.Localize("NotificationsPluginNotInstalledTitle", "Plugin not installed!"); + public static string Notifications_PluginNotInstalledTitle => + Loc.Localize("NotificationsPluginNotInstalledTitle", "Plugin not installed!"); - public static string Notifications_PluginNotInstalled(string name) => Loc.Localize("NotificationsPluginNotInstalled", "'{0}' failed to install.").Format(name); + public static string Notifications_PluginNotInstalled(string name) => + Loc.Localize("NotificationsPluginNotInstalled", "'{0}' failed to install.").Format(name); - public static string Notifications_NoUpdatesFoundTitle => Loc.Localize("NotificationsNoUpdatesFoundTitle", "No updates found!"); + public static string Notifications_NoUpdatesFoundTitle => + Loc.Localize("NotificationsNoUpdatesFoundTitle", "No updates found!"); - public static string Notifications_NoUpdatesFound => Loc.Localize("NotificationsNoUpdatesFound", "No updates were found."); + public static string Notifications_NoUpdatesFound => + Loc.Localize("NotificationsNoUpdatesFound", "No updates were found."); - public static string Notifications_UpdatesInstalledTitle => Loc.Localize("NotificationsUpdatesInstalledTitle", "Updates installed!"); + public static string Notifications_UpdatesInstalledTitle => + Loc.Localize("NotificationsUpdatesInstalledTitle", "Updates installed!"); - public static string Notifications_UpdatesInstalled(int count) => Loc.Localize("NotificationsUpdatesInstalled", "Updates for {0} of your plugins were installed.").Format(count); + public static string Notifications_UpdatesInstalled(int count) => Loc + .Localize("NotificationsUpdatesInstalled", + "Updates for {0} of your plugins were installed.") + .Format(count); - public static string Notifications_PluginDisabledTitle => Loc.Localize("NotificationsPluginDisabledTitle", "Plugin disabled!"); + public static string Notifications_PluginDisabledTitle => + Loc.Localize("NotificationsPluginDisabledTitle", "Plugin disabled!"); - public static string Notifications_PluginDisabled(string name) => Loc.Localize("NotificationsPluginDisabled", "'{0}' was disabled.").Format(name); + public static string Notifications_PluginDisabled(string name) => + Loc.Localize("NotificationsPluginDisabled", "'{0}' was disabled.").Format(name); - public static string Notifications_PluginEnabledTitle => Loc.Localize("NotificationsPluginEnabledTitle", "Plugin enabled!"); + public static string Notifications_PluginEnabledTitle => + Loc.Localize("NotificationsPluginEnabledTitle", "Plugin enabled!"); - public static string Notifications_PluginEnabled(string name) => Loc.Localize("NotificationsPluginEnabled", "'{0}' was enabled.").Format(name); + public static string Notifications_PluginEnabled(string name) => + Loc.Localize("NotificationsPluginEnabled", "'{0}' was enabled.").Format(name); #endregion @@ -3160,13 +3322,15 @@ internal class PluginInstallerWindow : Window, IDisposable public static string FooterButton_UpdatePlugins => Loc.Localize("InstallerUpdatePlugins", "Update plugins"); - public static string FooterButton_UpdateSafeMode => Loc.Localize("InstallerUpdateSafeMode", "Can't update in safe mode"); + public static string FooterButton_UpdateSafeMode => + Loc.Localize("InstallerUpdateSafeMode", "Can't update in safe mode"); public static string FooterButton_InProgress => Loc.Localize("InstallerInProgress", "Install in progress..."); public static string FooterButton_NoUpdates => Loc.Localize("InstallerNoUpdates", "No updates found!"); - public static string FooterButton_UpdateComplete(int count) => Loc.Localize("InstallerUpdateComplete", "{0} plugins updated!").Format(count); + public static string FooterButton_UpdateComplete(int count) => + Loc.Localize("InstallerUpdateComplete", "{0} plugins updated!").Format(count); public static string FooterButton_Settings => Loc.Localize("InstallerSettings", "Settings"); @@ -3180,7 +3344,10 @@ internal class PluginInstallerWindow : Window, IDisposable public static string UpdateModal_Title => Loc.Localize("UpdateQuestionModal", "Update Available"); - public static string UpdateModal_UpdateAvailable(string name) => Loc.Localize("UpdateModalUpdateAvailable", "An update for \"{0}\" is available.\nDo you want to update it before enabling?\nUpdates will fix bugs and incompatibilities, and may add new features.").Format(name); + public static string UpdateModal_UpdateAvailable(string name) => Loc + .Localize("UpdateModalUpdateAvailable", + "An update for \"{0}\" is available.\nDo you want to update it before enabling?\nUpdates will fix bugs and incompatibilities, and may add new features.") + .Format(name); public static string UpdateModal_Yes => Loc.Localize("UpdateModalYes", "Update plugin"); @@ -3196,29 +3363,63 @@ internal class PluginInstallerWindow : Window, IDisposable "InstallerContactAuthor", "Please restart your game and try again. If this error occurs again, please contact the plugin author."); - public static string ErrorModal_InstallFail(string name) => Loc.Localize("InstallerInstallFail", "Failed to install plugin {0}.\n{1}").Format(name, ErrorModal_InstallContactAuthor); + public static string ErrorModal_InstallFail(string name) => Loc + .Localize("InstallerInstallFail", + "Failed to install plugin {0}.\n{1}") + .Format(name, ErrorModal_InstallContactAuthor); - public static string ErrorModal_SingleUpdateFail(string name) => Loc.Localize("InstallerSingleUpdateFail", "Failed to update plugin {0}.\n{1}").Format(name, ErrorModal_InstallContactAuthor); + public static string ErrorModal_SingleUpdateFail(string name) => Loc + .Localize("InstallerSingleUpdateFail", + "Failed to update plugin {0}.\n{1}") + .Format(name, ErrorModal_InstallContactAuthor); - public static string ErrorModal_DeleteConfigFail(string name) => Loc.Localize("InstallerDeleteConfigFail", "Failed to reset the plugin {0}.\n\nThe plugin may not support this action. You can try deleting the configuration manually while the game is shut down - please see the FAQ.").Format(name); + public static string ErrorModal_DeleteConfigFail(string name) => Loc + .Localize("InstallerDeleteConfigFail", + "Failed to reset the plugin {0}.\n\nThe plugin may not support this action. You can try deleting the configuration manually while the game is shut down - please see the FAQ.") + .Format(name); - public static string ErrorModal_EnableFail(string name) => Loc.Localize("InstallerEnableFail", "Failed to enable plugin {0}.\n{1}").Format(name, ErrorModal_InstallContactAuthor); + public static string ErrorModal_EnableFail(string name) => Loc + .Localize("InstallerEnableFail", + "Failed to enable plugin {0}.\n{1}") + .Format(name, ErrorModal_InstallContactAuthor); - public static string ErrorModal_DisableFail(string name) => Loc.Localize("InstallerDisableFail", "Failed to disable plugin {0}.\n{1}").Format(name, ErrorModal_InstallContactAuthor); + public static string ErrorModal_DisableFail(string name) => Loc + .Localize("InstallerDisableFail", + "Failed to disable plugin {0}.\n{1}") + .Format(name, ErrorModal_InstallContactAuthor); - public static string ErrorModal_UnloadFail(string name) => Loc.Localize("InstallerUnloadFail", "Failed to unload plugin {0}.\n{1}").Format(name, ErrorModal_InstallContactAuthor); + public static string ErrorModal_UnloadFail(string name) => Loc + .Localize("InstallerUnloadFail", + "Failed to unload plugin {0}.\n{1}") + .Format(name, ErrorModal_InstallContactAuthor); - public static string ErrorModal_LoadFail(string name) => Loc.Localize("InstallerLoadFail", "Failed to load plugin {0}.\n{1}").Format(name, ErrorModal_InstallContactAuthor); + public static string ErrorModal_LoadFail(string name) => Loc + .Localize("InstallerLoadFail", + "Failed to load plugin {0}.\n{1}") + .Format(name, ErrorModal_InstallContactAuthor); - public static string ErrorModal_DeleteFail(string name) => Loc.Localize("InstallerDeleteFail", "Failed to delete plugin {0}.\n{1}").Format(name, ErrorModal_InstallContactAuthor); + public static string ErrorModal_DeleteFail(string name) => Loc + .Localize("InstallerDeleteFail", + "Failed to delete plugin {0}.\n{1}") + .Format(name, ErrorModal_InstallContactAuthor); - public static string ErrorModal_UpdaterFatal => Loc.Localize("InstallerUpdaterFatal", "Failed to update plugins.\nPlease restart your game and try again. If this error occurs again, please complain."); + public static string ErrorModal_UpdaterFatal => Loc.Localize("InstallerUpdaterFatal", + "Failed to update plugins.\nPlease restart your game and try again. If this error occurs again, please complain."); - public static string ErrorModal_UpdaterFail(int failCount) => Loc.Localize("InstallerUpdaterFail", "Failed to update {0} plugins.\nPlease restart your game and try again. If this error occurs again, please complain.").Format(failCount); + public static string ErrorModal_UpdaterFail(int failCount) => Loc + .Localize("InstallerUpdaterFail", + "Failed to update {0} plugins.\nPlease restart your game and try again. If this error occurs again, please complain.") + .Format(failCount); - public static string ErrorModal_UpdaterFailPartial(int successCount, int failCount) => Loc.Localize("InstallerUpdaterFailPartial", "Updated {0} plugins, failed to update {1}.\nPlease restart your game and try again. If this error occurs again, please complain.").Format(successCount, failCount); + public static string ErrorModal_UpdaterFailPartial(int successCount, int failCount) => Loc + .Localize("InstallerUpdaterFailPartial", + "Updated {0} plugins, failed to update {1}.\nPlease restart your game and try again. If this error occurs again, please complain.") + .Format(successCount, failCount); - public static string ErrorModal_HintBlame(string plugins) => Loc.Localize("InstallerErrorPluginInfo", "\n\nThe following plugins caused these issues:\n\n{0}\nYou may try removing these plugins manually and reinstalling them.").Format(plugins); + public static string ErrorModal_HintBlame(string plugins) => Loc + .Localize("InstallerErrorPluginInfo", + "\n\nThe following plugins caused these issues:\n\n{0}\nYou may try removing these plugins manually and reinstalling them.") + .Format(plugins); // public static string ErrorModal_Hint => Loc.Localize("InstallerErrorHint", "The plugin installer ran into an issue or the plugin is incompatible.\nPlease restart the game and report this error on our discord."); @@ -3228,43 +3429,71 @@ internal class PluginInstallerWindow : Window, IDisposable public static string FeedbackModal_Title => Loc.Localize("InstallerFeedback", "Send Feedback"); - public static string FeedbackModal_Text(string pluginName) => Loc.Localize("InstallerFeedbackInfo", "You can send feedback to the developer of \"{0}\" here.").Format(pluginName); + public static string FeedbackModal_Text(string pluginName) => Loc + .Localize("InstallerFeedbackInfo", + "You can send feedback to the developer of \"{0}\" here.") + .Format(pluginName); - public static string FeedbackModal_HasUpdate => Loc.Localize("InstallerFeedbackHasUpdate", "A new version of this plugin is available, please update before reporting bugs."); + public static string FeedbackModal_HasUpdate => Loc.Localize("InstallerFeedbackHasUpdate", + "A new version of this plugin is available, please update before reporting bugs."); - public static string FeedbackModal_ContactAnonymous => Loc.Localize("InstallerFeedbackContactAnonymous", "Submit feedback anonymously"); + public static string FeedbackModal_ContactAnonymous => + Loc.Localize("InstallerFeedbackContactAnonymous", "Submit feedback anonymously"); - public static string FeedbackModal_ContactAnonymousWarning => Loc.Localize("InstallerFeedbackContactAnonymousWarning", "No response will be forthcoming.\nUntick \"{0}\" and provide contact information if you need help.").Format(FeedbackModal_ContactAnonymous); + public static string FeedbackModal_ContactAnonymousWarning => Loc + .Localize( + "InstallerFeedbackContactAnonymousWarning", + "No response will be forthcoming.\nUntick \"{0}\" and provide contact information if you need help.") + .Format(FeedbackModal_ContactAnonymous); - public static string FeedbackModal_ContactInformation => Loc.Localize("InstallerFeedbackContactInfo", "Contact information"); + public static string FeedbackModal_ContactInformation => + Loc.Localize("InstallerFeedbackContactInfo", "Contact information"); - public static string FeedbackModal_ContactInformationHelp => Loc.Localize("InstallerFeedbackContactInfoHelp", "Discord usernames and e-mail addresses are accepted.\nIf you submit a Discord username, please join our discord server so that we can reach out to you easier."); + public static string FeedbackModal_ContactInformationHelp => Loc.Localize( + "InstallerFeedbackContactInfoHelp", + "Discord usernames and e-mail addresses are accepted.\nIf you submit a Discord username, please join our discord server so that we can reach out to you easier."); - public static string FeedbackModal_ContactInformationWarning => Loc.Localize("InstallerFeedbackContactInfoWarning", "Do not submit in-game character names."); + public static string FeedbackModal_ContactInformationWarning => + Loc.Localize("InstallerFeedbackContactInfoWarning", "Do not submit in-game character names."); - public static string FeedbackModal_ContactInformationRequired => Loc.Localize("InstallerFeedbackContactInfoRequired", "Contact information has not been provided. If you do not want to provide contact information, tick on \"{0}\" above.").Format(FeedbackModal_ContactAnonymous); + public static string FeedbackModal_ContactInformationRequired => Loc + .Localize( + "InstallerFeedbackContactInfoRequired", + "Contact information has not been provided. If you do not want to provide contact information, tick on \"{0}\" above.") + .Format(FeedbackModal_ContactAnonymous); - public static string FeedbackModal_ContactInformationDiscordButton => Loc.Localize("ContactInformationDiscordButton", "Join Goat Place Discord"); + public static string FeedbackModal_ContactInformationDiscordButton => + Loc.Localize("ContactInformationDiscordButton", "Join Goat Place Discord"); - public static string FeedbackModal_ContactInformationDiscordUrl => Loc.Localize("ContactInformationDiscordUrl", "https://goat.place/"); + public static string FeedbackModal_ContactInformationDiscordUrl => + Loc.Localize("ContactInformationDiscordUrl", "https://goat.place/"); - public static string FeedbackModal_IncludeLastError => Loc.Localize("InstallerFeedbackIncludeLastError", "Include last error message"); + public static string FeedbackModal_IncludeLastError => + Loc.Localize("InstallerFeedbackIncludeLastError", "Include last error message"); - public static string FeedbackModal_IncludeLastErrorHint => Loc.Localize("InstallerFeedbackIncludeLastErrorHint", "This option can give the plugin developer useful feedback on what exactly went wrong."); + public static string FeedbackModal_IncludeLastErrorHint => Loc.Localize( + "InstallerFeedbackIncludeLastErrorHint", + "This option can give the plugin developer useful feedback on what exactly went wrong."); - public static string FeedbackModal_Hint => Loc.Localize("InstallerFeedbackHint", "All plugin developers will be able to see your feedback.\nPlease never include any personal or revealing information.\nIf you chose to include the last error message, information like your Windows username may be included.\n\nThe collected feedback is not stored on our end and immediately relayed to Discord."); + public static string FeedbackModal_Hint => Loc.Localize("InstallerFeedbackHint", + "All plugin developers will be able to see your feedback.\nPlease never include any personal or revealing information.\nIf you chose to include the last error message, information like your Windows username may be included.\n\nThe collected feedback is not stored on our end and immediately relayed to Discord."); - public static string FeedbackModal_NotificationSuccess => Loc.Localize("InstallerFeedbackNotificationSuccess", "Your feedback was sent successfully!"); + public static string FeedbackModal_NotificationSuccess => + Loc.Localize("InstallerFeedbackNotificationSuccess", "Your feedback was sent successfully!"); - public static string FeedbackModal_NotificationError => Loc.Localize("InstallerFeedbackNotificationError", "Your feedback could not be sent."); + public static string FeedbackModal_NotificationError => + Loc.Localize("InstallerFeedbackNotificationError", "Your feedback could not be sent."); #endregion #region Testing Warning Modal - public static string TestingWarningModal_Title => Loc.Localize("InstallerTestingWarning", "Warning###InstallerTestingWarning"); + public static string TestingWarningModal_Title => + Loc.Localize("InstallerTestingWarning", "Warning###InstallerTestingWarning"); - public static string TestingWarningModal_DowngradeBody => Loc.Localize("InstallerTestingWarningDowngradeBody", "Take care! If you opt out of testing for a plugin, you will remain on the testing version until it is deleted and reinstalled, or the non-testing version of the plugin is updated.\nKeep in mind that you may lose the settings for this plugin if you downgrade manually."); + public static string TestingWarningModal_DowngradeBody => Loc.Localize( + "InstallerTestingWarningDowngradeBody", + "Take care! If you opt out of testing for a plugin, you will remain on the testing version until it is deleted and reinstalled, or the non-testing version of the plugin is updated.\nKeep in mind that you may lose the settings for this plugin if you downgrade manually."); #endregion @@ -3282,7 +3511,8 @@ internal class PluginInstallerWindow : Window, IDisposable #region Other - public static string SafeModeDisclaimer => Loc.Localize("SafeModeDisclaimer", "You enabled safe mode, no plugins will be loaded.\nYou may delete plugins from the \"Installed plugins\" tab.\nSimply restart your game to disable safe mode."); + public static string SafeModeDisclaimer => Loc.Localize("SafeModeDisclaimer", + "You enabled safe mode, no plugins will be loaded.\nYou may delete plugins from the \"Installed plugins\" tab.\nSimply restart your game to disable safe mode."); #endregion } From 29da285091b3a9d35c75fde6eae6caff12d3708c Mon Sep 17 00:00:00 2001 From: chirp Date: Sun, 19 Mar 2023 18:10:27 +0000 Subject: [PATCH 09/34] Add 'Oops, Maybe Lalafells!' --- Dalamud/Fools/FoolsManager.cs | 4 +- .../Fools/Plugins/OopsMaybeLalafellsPlugin.cs | 83 +++++++++++++++++++ lib/FFXIVClientStructs | 2 +- 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 3e74080df..8297bead3 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -77,8 +77,8 @@ internal class FoolsManager : IDisposable, IServiceType { new("Pixel Imperfect", "PixelImperfectPlugin", "Whoops... we messed up the math on that one.", "Halpo", typeof(PixelImperfectPlugin)), - new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", - "MidoriKami", typeof(DailyLifeDutyPlugin)), + new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", "MidoriKami", typeof(DailyLifeDutyPlugin)), + new("Oops, Maybe Lalafells!", "OopsMaybeLalafellsPlugin", "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.", "Chrip", typeof(OopsMaybeLalafells)) }; } diff --git a/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs new file mode 100644 index 000000000..b8f5f4f6e --- /dev/null +++ b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; + +using Dalamud.Game; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Hooking; + +using FFXIVClientStructs.FFXIV.Client.Game.Object; + +namespace Dalamud.Fools.Plugins; + +public class OopsMaybeLalafells : IFoolsPlugin +{ + // Plugin + + public OopsMaybeLalafells() + { + var scanner = Service.Get(); + var addr = scanner.ScanText(SetupCharacterSig); + SetupCharacterHook = Hook.FromAddress(addr, SetupCharacterDetour); + SetupCharacterHook.Enable(); + RedrawAll(); + } + + public void Dispose() + { + SetupCharacterHook.Disable(); + SetupCharacterHook.Dispose(); + RedrawAll(); + } + + private unsafe void RedrawAll() + { + Service.Get().RunOnFrameworkThread(() => { + var objects = Service.Get(); + foreach (var obj in objects) + { + if (obj.ObjectIndex > 241) break; + + var csObject = (GameObject*)obj.Address; + if (csObject == null) continue; + + csObject->DisableDraw(); + csObject->EnableDraw(); + } + }); + } + + // The Lalafellinator + + private readonly Random Rng = new(); + + private readonly List ReplaceIDs = new() { 84, 85, 86, 87, 88, 89, 90, 91, 257, 258, 581, 597, 744 }; + + private const string SetupCharacterSig = "E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B D7 E8 ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7"; + + private delegate char SetupCharacterDelegate(nint a1, nint a2); + private Hook SetupCharacterHook = null!; + + private unsafe char SetupCharacterDetour(nint a1, nint a2) + { + // Roll the dice + if (Rng.Next(0, 4) == 0) + { + var customize = (byte*)a2; + customize[(int)CustomizeIndex.Race] = 3; + + var face = customize + (int)CustomizeIndex.FaceType; + *face = (byte)(1 + ((*face - 1) % 4)); + + var equipTar = (ushort)(customize[(int)CustomizeIndex.Gender] == 0 ? 92 : 93); + for (var i = 1; i < 5; i++) + { + var equip = (ushort*)(a2 + 28 + (i * 4)); + if (ReplaceIDs.Contains(*equip)) + *equip = equipTar; + } + } + + return SetupCharacterHook.Original(a1, a2); + } +} diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 74345c974..9e97396f9 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 74345c97468f310d555929f4e40c844556bc64e7 +Subproject commit 9e97396f98c20eff17a71aacb24296efcaa04808 From 6cfb205bb8c9b1514feb7be78c7a60dacccdb515 Mon Sep 17 00:00:00 2001 From: NotNite Date: Sun, 19 Mar 2023 14:53:58 -0400 Subject: [PATCH 10/34] Add Screensaver plugin --- Dalamud/Fools/FoolsManager.cs | 3 +- Dalamud/Fools/Plugins/ScreensaverPlugin.cs | 68 ++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 Dalamud/Fools/Plugins/ScreensaverPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 8297bead3..2d82b72b0 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -78,7 +78,8 @@ internal class FoolsManager : IDisposable, IServiceType new("Pixel Imperfect", "PixelImperfectPlugin", "Whoops... we messed up the math on that one.", "Halpo", typeof(PixelImperfectPlugin)), new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", "MidoriKami", typeof(DailyLifeDutyPlugin)), - new("Oops, Maybe Lalafells!", "OopsMaybeLalafellsPlugin", "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.", "Chrip", typeof(OopsMaybeLalafells)) + new("Oops, Maybe Lalafells!", "OopsMaybeLalafellsPlugin", "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.", "Chrip", typeof(OopsMaybeLalafells)), + new("Screensaver", "ScreensaverPlugin", "Prevent burn-in on loading screens.", "NotNite", typeof(ScreensaverPlugin)), }; } diff --git a/Dalamud/Fools/Plugins/ScreensaverPlugin.cs b/Dalamud/Fools/Plugins/ScreensaverPlugin.cs new file mode 100644 index 000000000..af1c1b858 --- /dev/null +++ b/Dalamud/Fools/Plugins/ScreensaverPlugin.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using System.Numerics; +using Dalamud.Game.ClientState.Conditions; +using Dalamud.Interface; +using Dalamud.Interface.Internal; +using ImGuiNET; +using ImGuiScene; +using static ImGuiNET.ImGuiWindowFlags; + +namespace Dalamud.Fools.Plugins; + +public class ScreensaverPlugin : IFoolsPlugin +{ + private readonly TextureWrap logoTexture; + private readonly Condition condition; + + private int x; + private int y; + + private bool xDir = true; + private bool yDir = true; + + private double lastTime; + + public ScreensaverPlugin() + { + var interfaceManager = Service.Get(); + var dalamud = Service.Get(); + this.condition = Service.Get(); + + this.logoTexture = + interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "logo.png"))!; + } + + public void DrawUi() + { + var time = Environment.TickCount64 / 1000.0; + var diff = time - this.lastTime; + diff = diff > 1 ? 1 : diff; + this.lastTime = time; + + if (!this.condition[ConditionFlag.BetweenAreas]) + { + return; + } + + var textureSize = new Vector2(100); + var maxSize = ImGui.GetMainViewport().Size - textureSize; + this.xDir = this.xDir ? this.x < maxSize.X : this.x > 0; + this.yDir = this.yDir ? this.y < maxSize.Y : this.y > 0; + + this.x += (int)(diff * (this.xDir ? 1 : -1) * 100); + this.y += (int)(diff * (this.yDir ? 1 : -1) * 100); + + ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(0, 0)); + ImGuiHelpers.ForceNextWindowMainViewport(); + ImGuiHelpers.SetNextWindowPosRelativeMainViewport(new Vector2(this.x, this.y)); + ImGui.Begin("Screensaver", NoInputs | NoNav | NoTitleBar | NoScrollbar | NoBackground); + ImGui.SetWindowSize(textureSize); + ImGui.Image(this.logoTexture.ImGuiHandle, textureSize); + } + + public void Dispose() + { + this.logoTexture.Dispose(); + } +} From 4e8cb79e8982b9ffde71f5af4d93b2c039e613b3 Mon Sep 17 00:00:00 2001 From: NotNite Date: Sun, 19 Mar 2023 14:55:25 -0400 Subject: [PATCH 11/34] Add attribution to Screensaver and OML --- Dalamud/Fools/FoolsManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 2d82b72b0..0a2a8c99c 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -46,8 +46,9 @@ namespace Dalamud.Fools; // // April fools! <3 // This was a group effort in one week lead by: -// NotNite: FoolsManager, Pixel Imperfect +// NotNite: FoolsManager, Pixel Imperfect, Screensaver // Berna: DailyLifeDuty +// Chirp: Oops, Maybe Lalafells! /// /// Manager for all the IFoolsPlugin instances. From fe6638d17216f74efbe5dcdf8d5190cdbc0d9cca Mon Sep 17 00:00:00 2001 From: NotNite Date: Sun, 19 Mar 2023 15:04:41 -0400 Subject: [PATCH 12/34] I'm dumb as bricks --- Dalamud/Fools/Plugins/ScreensaverPlugin.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dalamud/Fools/Plugins/ScreensaverPlugin.cs b/Dalamud/Fools/Plugins/ScreensaverPlugin.cs index af1c1b858..ea26c7634 100644 --- a/Dalamud/Fools/Plugins/ScreensaverPlugin.cs +++ b/Dalamud/Fools/Plugins/ScreensaverPlugin.cs @@ -59,6 +59,9 @@ public class ScreensaverPlugin : IFoolsPlugin ImGui.Begin("Screensaver", NoInputs | NoNav | NoTitleBar | NoScrollbar | NoBackground); ImGui.SetWindowSize(textureSize); ImGui.Image(this.logoTexture.ImGuiHandle, textureSize); + + ImGui.End(); + ImGui.PopStyleVar(); } public void Dispose() From 6403c8af682f58afa252f033ec65b7afcf4e5446 Mon Sep 17 00:00:00 2001 From: chirp Date: Mon, 20 Mar 2023 00:48:56 +0000 Subject: [PATCH 13/34] Add 'Cat Bubbles', fix crash in OML --- Dalamud/Fools/FoolsManager.cs | 1 + Dalamud/Fools/Plugins/CatBubblesPlugin.cs | 135 ++++++++++++++++++ .../Fools/Plugins/OopsMaybeLalafellsPlugin.cs | 54 +++++-- 3 files changed, 175 insertions(+), 15 deletions(-) create mode 100644 Dalamud/Fools/Plugins/CatBubblesPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 0a2a8c99c..1509acb4e 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -81,6 +81,7 @@ internal class FoolsManager : IDisposable, IServiceType new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", "MidoriKami", typeof(DailyLifeDutyPlugin)), new("Oops, Maybe Lalafells!", "OopsMaybeLalafellsPlugin", "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.", "Chrip", typeof(OopsMaybeLalafells)), new("Screensaver", "ScreensaverPlugin", "Prevent burn-in on loading screens.", "NotNite", typeof(ScreensaverPlugin)), + new("Cat Bubbles", "CatBubblesPlugin", "Enables in-game sdfgasdfgkljewriogdfkjghahfvcxbnmlqpwoeiruty", "Chirp's Cat, Sir Fluffington III", typeof(CatBubblesPlugin)) }; } diff --git a/Dalamud/Fools/Plugins/CatBubblesPlugin.cs b/Dalamud/Fools/Plugins/CatBubblesPlugin.cs new file mode 100644 index 000000000..3016a465e --- /dev/null +++ b/Dalamud/Fools/Plugins/CatBubblesPlugin.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Timers; + +using Dalamud.Game; +using Dalamud.Game.ClientState; +using Dalamud.Hooking; + +using FFXIVClientStructs.FFXIV.Client.Game; + +namespace Dalamud.Fools.Plugins; + +public class CatBubblesPlugin : IFoolsPlugin +{ + // Plugin + + private ClientState ClientState; + + public CatBubblesPlugin() + { + ClientState = Service.Get(); + + var sigscanner = Service.Get(); + + var openAddr = sigscanner.ScanText("E8 ?? ?? ?? ?? C7 43 ?? ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ??"); + BalloonOpen = Marshal.GetDelegateForFunctionPointer(openAddr); + + var updateAddr = sigscanner.ScanText("48 85 D2 0F 84 ?? ?? ?? ?? 48 89 5C 24 ?? 57 48 83 EC 20 8B 41 0C"); + BalloonUpdateHook = Hook.FromAddress(updateAddr, BalloonUpdateDetour); + BalloonUpdateHook.Enable(); + + Timer.Elapsed += OnTimerElapsed; + Timer.Interval = Rng.Next(3, 8) * 1000; + Timer.Start(); + } + + public void Dispose() + { + Timer.Elapsed -= OnTimerElapsed; + Timer.Stop(); + + BalloonUpdateHook.Disable(); + BalloonUpdateHook.Dispose(); + } + + private void OnTimerElapsed(object sender, object e) + { + EngageCatMode = true; + Timer.Interval = Rng.Next(35, 150) * 1000; + } + + // meow :3 + + private bool EngageCatMode = false; + + private readonly Timer Timer = new(); + private readonly Random Rng = new(); + + private readonly List strs1 = new() { "mrrp", "nya", "mew", "meow", "mraow", "purr" }; + private readonly List strs2 = new() { ":3", ":3c", "=^-^=" }; + private readonly List strs3 = new() { "zxcvbnm,./`-=", "qweasdzxc", "fghjkl;mnbvcxz", "plokmijnuhkjgs" }; + + private string GetRandStr(List list) + { + var x = Rng.Next(list.Count); + return list[x]; + } + + private string GenerateCatSpeak() + { + var items = new List(); + + var itemCt = Rng.Next(1, 10) + 1; + + int lastGen = -1; + bool hasEmoted = false; + for (var i = 0; i < itemCt; i++) + { + var isLast = i == itemCt - 1; + + var r = i == 0 ? 0 : Rng.Next(0, 3); + switch (r) + { + case 0: + items.Add(GetRandStr(strs1)); + break; + case 1: + if (hasEmoted && !isLast) goto case default; + var item = GetRandStr(strs2); + if (lastGen == 0) item = ' ' + item; + if (!isLast) item += ' '; + items.Add(item); + hasEmoted = true; + break; + case 2: + if (isLast && lastGen != 1) goto case 1; + if (lastGen != 0) goto case default; + items.Add(" "); + break; + default: + items.Add(GetRandStr(strs3)); + break; + } + + lastGen = r; + } + + return string.Join("", items); + } + + private delegate nint BalloonOpenDelegate(nint a1, nint a2, string a3, bool a4); + private BalloonOpenDelegate BalloonOpen; + + private delegate nint BalloonUpdateDelegate(nint a1, nint a2, nint a3, nint a4); + private Hook BalloonUpdateHook = null!; + + private unsafe nint BalloonUpdateDetour(nint a1, nint a2, nint a3, nint a4) + { + var balloon = (Balloon*)a1; + if (EngageCatMode && a2 == ClientState.LocalPlayer?.Address && balloon->State == BalloonState.Inactive) + { + var text = GenerateCatSpeak(); + balloon->Text.SetString(text); + balloon->State = BalloonState.Active; + balloon->Type = BalloonType.Timer; + balloon->PlayTimer = 5f; + BalloonOpen(a1, a2, text, balloon->UnkBool == 1); + + EngageCatMode = false; + } + + return BalloonUpdateHook.Original(a1, a2, a3, a4); + } +} diff --git a/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs index b8f5f4f6e..29982ba65 100644 --- a/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs +++ b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using Dalamud.Game; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Hooking; +using Dalamud.Logging; using FFXIVClientStructs.FFXIV.Client.Game.Object; @@ -27,6 +29,7 @@ public class OopsMaybeLalafells : IFoolsPlugin { SetupCharacterHook.Disable(); SetupCharacterHook.Dispose(); + RedrawAll(); } @@ -36,7 +39,7 @@ public class OopsMaybeLalafells : IFoolsPlugin var objects = Service.Get(); foreach (var obj in objects) { - if (obj.ObjectIndex > 241) break; + if (obj.ObjectIndex > 241 && obj.ObjectIndex < 301) continue; var csObject = (GameObject*)obj.Address; if (csObject == null) continue; @@ -54,30 +57,51 @@ public class OopsMaybeLalafells : IFoolsPlugin private readonly List ReplaceIDs = new() { 84, 85, 86, 87, 88, 89, 90, 91, 257, 258, 581, 597, 744 }; private const string SetupCharacterSig = "E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B D7 E8 ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7"; - private delegate char SetupCharacterDelegate(nint a1, nint a2); private Hook SetupCharacterHook = null!; - private unsafe char SetupCharacterDetour(nint a1, nint a2) + private char SetupCharacterDetour(nint a1, nint a2) { - // Roll the dice - if (Rng.Next(0, 4) == 0) + try { - var customize = (byte*)a2; - customize[(int)CustomizeIndex.Race] = 3; - - var face = customize + (int)CustomizeIndex.FaceType; - *face = (byte)(1 + ((*face - 1) % 4)); + var custom = Marshal.PtrToStructure(a2); - var equipTar = (ushort)(customize[(int)CustomizeIndex.Gender] == 0 ? 92 : 93); - for (var i = 1; i < 5; i++) + // Roll the dice + if (custom.Race != 3 && Rng.Next(0, 4) == 0) { - var equip = (ushort*)(a2 + 28 + (i * 4)); - if (ReplaceIDs.Contains(*equip)) - *equip = equipTar; + custom.Race = 3; + custom.Tribe = (byte)(((custom.Race * 2) - 1) + 1 - (custom.Tribe % 2)); + custom.FaceType = (byte)(1 + ((custom.FaceType - 1) % 4)); + custom.ModelType %= 2; + Marshal.StructureToPtr(custom, a2, true); + + var equipTar = (ushort)(custom.Gender == 0 ? 92 : 93); + for (var i = 1; i < 5; i++) + { + var ofs = a2 + 28 + (i * 4); + var equip = (ushort)Marshal.ReadInt16(ofs); + if (ReplaceIDs.Contains(equip)) + Marshal.WriteInt16(ofs, (short)equipTar); + } } } + catch (Exception e) + { + PluginLog.Error(e.ToString(), e); + } return SetupCharacterHook.Original(a1, a2); } + + // Customize shit + + [StructLayout(LayoutKind.Explicit)] + private struct CustomizeData + { + [FieldOffset((int)CustomizeIndex.FaceType)] public byte FaceType; + [FieldOffset((int)CustomizeIndex.ModelType)] public byte ModelType; + [FieldOffset((int)CustomizeIndex.Race)] public byte Race; + [FieldOffset((int)CustomizeIndex.Tribe)] public byte Tribe; + [FieldOffset((int)CustomizeIndex.Gender)] public byte Gender; + } } From 171c281cca90d7073397fcdc7732d3c9c0e1a9b1 Mon Sep 17 00:00:00 2001 From: NotNite Date: Mon, 20 Mar 2023 11:09:11 -0400 Subject: [PATCH 14/34] Refactor FoolsPluginMetadata --- Dalamud/Fools/FoolsManager.cs | 55 +++++++++++++++---- Dalamud/Fools/FoolsPluginMetadata.cs | 19 ++----- .../PluginInstaller/PluginInstallerWindow.cs | 4 ++ 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 1509acb4e..9d76b4f06 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -45,10 +45,6 @@ namespace Dalamud.Fools; // * //(/******,,,.,,..,,,****/*///((((#(###(/##(, // // April fools! <3 -// This was a group effort in one week lead by: -// NotNite: FoolsManager, Pixel Imperfect, Screensaver -// Berna: DailyLifeDuty -// Chirp: Oops, Maybe Lalafells! /// /// Manager for all the IFoolsPlugin instances. @@ -76,12 +72,51 @@ internal class FoolsManager : IDisposable, IServiceType // reflect over all IFoolsPlugin implementations sometime(?) this.FoolsPlugins = new List { - new("Pixel Imperfect", "PixelImperfectPlugin", "Whoops... we messed up the math on that one.", "Halpo", - typeof(PixelImperfectPlugin)), - new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", "MidoriKami", typeof(DailyLifeDutyPlugin)), - new("Oops, Maybe Lalafells!", "OopsMaybeLalafellsPlugin", "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.", "Chrip", typeof(OopsMaybeLalafells)), - new("Screensaver", "ScreensaverPlugin", "Prevent burn-in on loading screens.", "NotNite", typeof(ScreensaverPlugin)), - new("Cat Bubbles", "CatBubblesPlugin", "Enables in-game sdfgasdfgkljewriogdfkjghahfvcxbnmlqpwoeiruty", "Chirp's Cat, Sir Fluffington III", typeof(CatBubblesPlugin)) + new() + { + Name = "Pixel Imperfect", + InternalName = "PixelImperfectPlugin", + Description = "Whoops... we messed up the math on that one.", + Author = "Halpo", + RealAuthor = "NotNite", + Type = typeof(PixelImperfectPlugin), + }, + new() + { + Name = "DailyLifeDuty", + InternalName = "DailyLifeDutyPlugin", + Description = "Easily Track Daily and Weekly tasks... in real life", + Author = "MidoriKami", + RealAuthor = "Berna", + Type = typeof(DailyLifeDutyPlugin), + }, + new() + { + Name = "Oops, Maybe Lalafells!", + InternalName = "OopsMaybeLalafellsPlugin", + Description = "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.", + Author = "Chrip", + RealAuthor = "Chirp", + Type = typeof(OopsMaybeLalafells), + }, + new() + { + Name = "Screensaver", + InternalName = "ScreensaverPlugin", + Description = "Prevent burn-in on loading screens.", + Author = "NotNite", + RealAuthor = "NotNite", + Type = typeof(ScreensaverPlugin), + }, + new() + { + Name = "Cat Bubbles", + InternalName = "CatBubblesPlugin", + Description = "Enables in-game sdfgasdfgkljewriogdfkjghahfvcxbnmlqpwoeiruty", + Author = "Chirp's Cat, Sir Fluffington III", + RealAuthor = "Chirp", + Type = typeof(CatBubblesPlugin), + }, }; } diff --git a/Dalamud/Fools/FoolsPluginMetadata.cs b/Dalamud/Fools/FoolsPluginMetadata.cs index 25c095a60..67c657400 100644 --- a/Dalamud/Fools/FoolsPluginMetadata.cs +++ b/Dalamud/Fools/FoolsPluginMetadata.cs @@ -4,22 +4,15 @@ namespace Dalamud.Fools; public class FoolsPluginMetadata { - public string Name { get; } + public string Name { get; init; } - public string InternalName { get; } + public string InternalName { get; init; } - public string Description { get; } + public string Description { get; init; } - public string Author { get; } + public string Author { get; init; } - public Type Type { get; } + public string RealAuthor { get; init; } - public FoolsPluginMetadata(string name, string internalName, string description, string author, Type type) - { - this.Name = name; - this.InternalName = internalName; - this.Description = description; - this.Author = author; - this.Type = type; - } + public Type Type { get; init; } } diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 451c9f2f2..4e308455e 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -1303,6 +1303,10 @@ internal class PluginInstallerWindow : Window, IDisposable ImGui.SameLine(); ImGui.TextColored(ImGuiColors.DalamudGrey3, $" by {plugin.Author}"); + if (ImGui.IsItemHovered() && plugin.Author != plugin.RealAuthor) + { + ImGui.SetTooltip($"actually by {plugin.RealAuthor}"); + } ImGui.TextWrapped(plugin.Description); From 69f1860a6d692c6b951dcfbb83c02013867c4e83 Mon Sep 17 00:00:00 2001 From: Bernardo Lopes Date: Mon, 20 Mar 2023 17:46:01 -0300 Subject: [PATCH 15/34] [Fools-DailyLifeDuty] Added more duties. --- Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs b/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs index 35ea47a99..c3b8364ed 100644 --- a/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs +++ b/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs @@ -13,10 +13,14 @@ public class DailyLifeDutyPlugin : IFoolsPlugin private static readonly List Duties = new[] { - new Duty("Dishes", i => $"{i} dishes to be cleaned"), + new Duty("Dishes", i => $"{i} dish(es) to be cleaned"), new Duty("Taxes", _ => "Taxes need to be filed"), - new Duty("Pets", i => $"{i} dogs waiting to be pet"), - new Duty("Garbage", i => $"{i} garbage bags to be put out"), + new Duty("Pets", i => $"{i} dog(s) waiting to be pet"), + new Duty("Garbage", i => $"{i} garbage bag(s) to be put out"), + new Duty("Bank", i => $"{i} bill(s) waiting payment"), + new Duty("Hydration", i => $"{i} glasses(s) of water remaining to reach Full Hydration"), + + // new Duty("FINAL FANTASY XIV", i => $"At least {i} minute(s) left on your sub... maybe. Time is relative."), }.ToList(); private long lastMessage; From 175651ac8864a54bf5b65f79ff90d82ee70818ae Mon Sep 17 00:00:00 2001 From: NotNite Date: Mon, 20 Mar 2023 17:59:24 -0400 Subject: [PATCH 16/34] Add YesSoliciting I didn't test this lol --- Dalamud/Fools/FoolsManager.cs | 9 ++ Dalamud/Fools/Plugins/YesSolicitingPlugin.cs | 100 +++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 Dalamud/Fools/Plugins/YesSolicitingPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 9d76b4f06..14f202e94 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -117,6 +117,15 @@ internal class FoolsManager : IDisposable, IServiceType RealAuthor = "Chirp", Type = typeof(CatBubblesPlugin), }, + new() + { + Name = "YesSoliciting", + InternalName = "YesSolicitingPlugin", + Description = "Summon annoying shout messages from beyond the rift.", + Author = "Anna", + RealAuthor = "NotNite", + Type = typeof(YesSolicitingPlugin), + }, }; } diff --git a/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs b/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs new file mode 100644 index 000000000..8058e7d87 --- /dev/null +++ b/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using Dalamud.Data; +using Dalamud.Game; +using Dalamud.Game.Gui; +using Dalamud.Game.Text; +using Dalamud.Utility; +using Lumina.Excel.GeneratedSheets; + +namespace Dalamud.Fools.Plugins; + +public class YesSolicitingPlugin : IFoolsPlugin +{ + private readonly Framework framework; + private readonly ChatGui chatGui; + + private readonly List firstNames; + private readonly List lastNames; + + private long nextUpdate; + + public YesSolicitingPlugin() + { + this.framework = Service.Get(); + this.framework.Update += this.OnUpdate; + + this.chatGui = Service.Get(); + + var dataManager = Service.Get(); + var charaMakeName = dataManager.GetExcelSheet()!; + + this.firstNames = new List(); + this.lastNames = new List(); + + for (uint i = 0; i < charaMakeName.RowCount; i++) + { + var row = charaMakeName.GetRow(i); + if (row == null) + { + break; + } + + // moon cats best cats, fight me + var firstName = row.MiqoteMoonFemale.ToDalamudString().TextValue; + var lastName = row.MiqoteMoonLastname.ToDalamudString().TextValue; + + if (firstName.Trim() == string.Empty || lastName.Trim() == string.Empty) + { + break; + } + + this.firstNames.Add(firstName); + this.lastNames.Add(lastName); + } + } + + public void Dispose() + { + this.framework.Update -= this.OnUpdate; + } + + private void Emit() + { + var firstName = this.firstNames[Random.Shared.Next(0, this.firstNames.Count)]; + var lastName = this.lastNames[Random.Shared.Next(0, this.lastNames.Count)]; + + var messages = new List + { + // legally required to put "april fools" in each of these so someone doesn't go outing themselves + "//**goat2023;PLUGIN SELLING FAST AND CHEAP;20 MINUTE WAIT TIME;APRIL FOOLS JOKE;https://goatcorp.github.io/;**//", + "(GOATCORP.GITHUB.IO) Buy April Fools Joke, Cheap FFXIV Plugins, Fast shipping ~-!@#", + "Need plugins?|[GOATCORP.GITHUB.IO]|10mins Delivery time|CheapFast100%|[CODE:APRILFOOLS,2023%OFF]", + "GOATCORP.GITHUB.IO - 10min Delivery time - Cheap - Fast - 100% - CODE:APRILFOOLS,2023%OFF", + + "Like to ERP? Join our Extraordinary Raid Party today!", + "Bored? Hungry? Visit the Alternate Reality Plugins section today!", + "Need to protect your eyes from stingy bugs? Get BeeShade! Now with 39% less malware.", + "Selling iTomestone 14 Pro - has 0.5x mechanic zoom camera, /tell if interested", + "buying gf 10k gil", + }; + + var message = messages[Random.Shared.Next(0, messages.Count)]; + + this.chatGui.PrintChat(new XivChatEntry + { + Message = $"[YesSoliciting] {firstName} {lastName}: {message}", + Type = XivChatType.Shout, + }); + } + + private void OnUpdate(Framework fr) + { + var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + if (now >= this.nextUpdate) + { + this.Emit(); + this.nextUpdate = now + (60 * 10); + } + } +} From 382a1c0c96b30362bfa4eba422494ca44c7ce73b Mon Sep 17 00:00:00 2001 From: chirp Date: Tue, 21 Mar 2023 06:55:45 +0000 Subject: [PATCH 17/34] Add 'Good Vibes' --- Dalamud/Fools/FoolsManager.cs | 11 +++++- Dalamud/Fools/Plugins/GoodVibesPlugin.cs | 46 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 Dalamud/Fools/Plugins/GoodVibesPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 14f202e94..9fa10b0e9 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -95,7 +95,7 @@ internal class FoolsManager : IDisposable, IServiceType Name = "Oops, Maybe Lalafells!", InternalName = "OopsMaybeLalafellsPlugin", Description = "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.", - Author = "Chrip", + Author = "Chirpopo Chirpo", RealAuthor = "Chirp", Type = typeof(OopsMaybeLalafells), }, @@ -126,6 +126,15 @@ internal class FoolsManager : IDisposable, IServiceType RealAuthor = "NotNite", Type = typeof(YesSolicitingPlugin), }, + new() + { + Name = "Good Vibes", + InternalName = "GoodVibesPlugin", + Description = "Shake things up with this vibe plugin!", + Author = "C h i r p", + RealAuthor = "Chirp", + Type = typeof(GoodVibesPlugin), + }, }; } diff --git a/Dalamud/Fools/Plugins/GoodVibesPlugin.cs b/Dalamud/Fools/Plugins/GoodVibesPlugin.cs new file mode 100644 index 000000000..aff2a0182 --- /dev/null +++ b/Dalamud/Fools/Plugins/GoodVibesPlugin.cs @@ -0,0 +1,46 @@ +using System; +using System.Numerics; + +using Dalamud.Game; +using Dalamud.Hooking; + +namespace Dalamud.Fools.Plugins +{ + public class GoodVibesPlugin : IFoolsPlugin + { + // Plugin + + public GoodVibesPlugin() + { + var addr = Service.Get().ScanText("48 83 EC 08 8B 02"); + OffsetModelHook = Hook.FromAddress(addr, OffsetModelDetour); + OffsetModelHook.Enable(); + } + + public void Dispose() + { + OffsetModelHook.Disable(); + OffsetModelHook.Dispose(); + } + + // brrrrrrrr + + private readonly Random Rng = new(); + + private Vector3 GenRandVec() + { + var Pos = new float[3]; + for (var i = 0; i < 3; i++) + Pos[i] = Rng.Next(-5, 5) / 1000f; + return new Vector3(Pos); + } + + private delegate nint OffsetModelDelegate(nint a1, nint a2); + private Hook OffsetModelHook = null!; + private unsafe nint OffsetModelDetour(nint a1, nint a2) + { + *(Vector3*)a2 += GenRandVec(); + return OffsetModelHook.Original(a1, a2); + } + } +} From 197abfeb0bb24e3d12cc7260fa4ac0dd87877919 Mon Sep 17 00:00:00 2001 From: chirp Date: Tue, 21 Mar 2023 07:04:16 +0000 Subject: [PATCH 18/34] Replace sig with manual offset for OML --- Dalamud/Fools/FoolsManager.cs | 2 +- Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 9fa10b0e9..b85c2ee24 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -128,7 +128,7 @@ internal class FoolsManager : IDisposable, IServiceType }, new() { - Name = "Good Vibes", + Name = "GoodVibes", InternalName = "GoodVibesPlugin", Description = "Shake things up with this vibe plugin!", Author = "C h i r p", diff --git a/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs index 29982ba65..1b0bce47d 100644 --- a/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs +++ b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs @@ -19,7 +19,7 @@ public class OopsMaybeLalafells : IFoolsPlugin public OopsMaybeLalafells() { var scanner = Service.Get(); - var addr = scanner.ScanText(SetupCharacterSig); + var addr = scanner.Module.BaseAddress + 0x0484F60; SetupCharacterHook = Hook.FromAddress(addr, SetupCharacterDetour); SetupCharacterHook.Enable(); RedrawAll(); @@ -55,8 +55,6 @@ public class OopsMaybeLalafells : IFoolsPlugin private readonly Random Rng = new(); private readonly List ReplaceIDs = new() { 84, 85, 86, 87, 88, 89, 90, 91, 257, 258, 581, 597, 744 }; - - private const string SetupCharacterSig = "E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B D7 E8 ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7"; private delegate char SetupCharacterDelegate(nint a1, nint a2); private Hook SetupCharacterHook = null!; From 4dbf27089a32e0ef8a2f3fa1a174cf888034ee79 Mon Sep 17 00:00:00 2001 From: Bernardo Lopes Date: Mon, 20 Mar 2023 10:46:03 -0300 Subject: [PATCH 19/34] Create framework for YesHealMe (AU NoTankYou). --- Dalamud/Fools/FoolsManager.cs | 10 +++ Dalamud/Fools/Helper/YesHealMe/HudHelper.cs | 41 ++++++++++++ .../Fools/Helper/YesHealMe/PartyListAddon.cs | 67 +++++++++++++++++++ .../Helper/YesHealMe/PartyListAddonData.cs | 37 ++++++++++ Dalamud/Fools/Plugins/YesHealMePlugin.cs | 32 +++++++++ 5 files changed, 187 insertions(+) create mode 100644 Dalamud/Fools/Helper/YesHealMe/HudHelper.cs create mode 100644 Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs create mode 100644 Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs create mode 100644 Dalamud/Fools/Plugins/YesHealMePlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index b85c2ee24..a8fa3d624 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -135,6 +135,16 @@ internal class FoolsManager : IDisposable, IServiceType RealAuthor = "Chirp", Type = typeof(GoodVibesPlugin), }, + new() + { + Name = "YesHealMe", + InternalName = "YesHealMePlugin", + Description = "The only warning you need.", + Author = "MidoriKami", + RealAuthor = "Berna", + Type = typeof(YesHealMePlugin), + }, + }; } diff --git a/Dalamud/Fools/Helper/YesHealMe/HudHelper.cs b/Dalamud/Fools/Helper/YesHealMe/HudHelper.cs new file mode 100644 index 000000000..6de258c6e --- /dev/null +++ b/Dalamud/Fools/Helper/YesHealMe/HudHelper.cs @@ -0,0 +1,41 @@ +using Dalamud; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.SubKinds; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; + +namespace NoTankYou.Utilities; + +public static unsafe class HudHelper +{ + private static AgentHUD* AgentHud => AgentModule.Instance()->GetAgentHUD(); + + public static PlayerCharacter? GetPlayerCharacter(int index) + { + // Sorta temporary, waiting for ClientStructs to merge a fixed size array for this element + var partyMemberList = AgentHud->PartyMemberList; + var targetOffset = index * sizeof(HudPartyMember); + var targetAddress = partyMemberList + targetOffset; + var hudData = (HudPartyMember*) targetAddress; + + var targetPlayer = hudData->ObjectId; + + return GetPlayer(targetPlayer); + } + + public static PlayerCharacter? GetAllianceMember(int index) + { + var targetPlayer = AgentHud->RaidMemberIds[index]; + + return GetPlayer(targetPlayer); + } + + private static PlayerCharacter? GetPlayer(uint objectId) + { + var result = Service.Get().SearchById(objectId); + + if (result?.GetType() == typeof(PlayerCharacter)) + return result as PlayerCharacter; + + return null; + } +} diff --git a/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs b/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs new file mode 100644 index 000000000..a2b106b40 --- /dev/null +++ b/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Dalamud; +using Dalamud.Game; +using Dalamud.Game.Gui; +using FFXIVClientStructs.FFXIV.Client.UI; +using NoTankYou.DataModels; +using NoTankYou.Utilities; + +namespace NoTankYou.System; + +public unsafe class PartyListAddon : IEnumerable, IDisposable +{ + public record PartyFramePositionInfo(Vector2 Position, Vector2 Size, Vector2 Scale); + public IEnumerator GetEnumerator() => addonData.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private static AddonPartyList* PartyList => (AddonPartyList*) Service.Get()?.GetAddonByName("_PartyList"); + public static bool DataAvailable => PartyList != null && PartyList->AtkUnitBase.RootNode != null; + + private readonly List addonData = new(); + + public PartyListAddon() + { + Service.Get().Update += OnFrameworkUpdate; + } + + public void Dispose() + { + Service.Get().Update -= OnFrameworkUpdate; + } + + private void OnFrameworkUpdate(Framework framework) + { + addonData.Clear(); + if (!DataAvailable) return; + if (PartyList->MemberCount <= 0) return; + + foreach (var index in Enumerable.Range(0, PartyList->MemberCount)) + { + var playerCharacter = HudHelper.GetPlayerCharacter(index); + var userInterface = PartyList->PartyMember[index]; + + addonData.Add(new PartyListAddonData + { + PlayerCharacter = playerCharacter, + UserInterface = userInterface, + }); + } + } + + public static PartyFramePositionInfo GetPositionInfo() + { + // Resource Node (id 9) contains a weird offset for the actual list elements + var rootNode = PartyList->AtkUnitBase.RootNode; + var addonBasePosition = new Vector2(rootNode->X, rootNode->Y); + var scale = new Vector2(rootNode->ScaleX, rootNode->ScaleY); + + var partyListNode = PartyList->AtkUnitBase.GetNodeById(9); + var partyListPositionOffset = new Vector2(partyListNode->X, partyListNode->Y) * scale; + var partyListSize = new Vector2(partyListNode->Width, partyListNode->Height); + + return new PartyFramePositionInfo(addonBasePosition + partyListPositionOffset, partyListSize * scale, scale); + } +} diff --git a/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs b/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs new file mode 100644 index 000000000..b9989ab0b --- /dev/null +++ b/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Dalamud.Game.ClientState.Objects.SubKinds; +using FFXIVClientStructs.FFXIV.Client.UI; + +namespace NoTankYou.DataModels; + +public readonly unsafe struct PartyListAddonData +{ + private static readonly Dictionary TimeSinceLastTargetable = new(); + + public AddonPartyList.PartyListMemberStruct UserInterface { get; init; } + public PlayerCharacter? PlayerCharacter { get; init; } + + private bool Targetable => UserInterface.PartyMemberComponent->OwnerNode->AtkResNode.Color.A != 0x99; + + public bool IsTargetable() + { + if (PlayerCharacter is null) return false; + + TimeSinceLastTargetable.TryAdd(PlayerCharacter.ObjectId, Stopwatch.StartNew()); + var stopwatch = TimeSinceLastTargetable[PlayerCharacter.ObjectId]; + + if (Targetable) + { + // Returns true if the party member has been targetable for 2second or more + return stopwatch.Elapsed >= TimeSpan.FromSeconds(2); + } + else + { + // Returns false, and continually resets the stopwatch + stopwatch.Restart(); + return false; + } + } +} \ No newline at end of file diff --git a/Dalamud/Fools/Plugins/YesHealMePlugin.cs b/Dalamud/Fools/Plugins/YesHealMePlugin.cs new file mode 100644 index 000000000..5b416b0a6 --- /dev/null +++ b/Dalamud/Fools/Plugins/YesHealMePlugin.cs @@ -0,0 +1,32 @@ +using System.Linq; +using Dalamud.Logging; +using NoTankYou.System; + +namespace Dalamud.Fools.Plugins; + +public class YesHealMePlugin: IFoolsPlugin +{ + private PartyListAddon partyListAddon; + + public YesHealMePlugin() + { + partyListAddon = new PartyListAddon(); + } + + public void DrawUi() + { + foreach (var partyMember in this.partyListAddon.Select(pla => pla.PlayerCharacter).Where(pc => pc is not null)) + { + if (partyMember.CurrentHp < partyMember.MaxHp) + { + // Do things here + } + } + } + + + public void Dispose() + { + this.partyListAddon.Dispose(); + } +} From dd2b842723f88082449c76362c157b34841770db Mon Sep 17 00:00:00 2001 From: Bernardo Lopes Date: Mon, 20 Mar 2023 11:12:12 -0300 Subject: [PATCH 20/34] Change DailyLifeDuty description. --- Dalamud/Fools/FoolsManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index a8fa3d624..d81d1dbe6 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -85,7 +85,7 @@ internal class FoolsManager : IDisposable, IServiceType { Name = "DailyLifeDuty", InternalName = "DailyLifeDutyPlugin", - Description = "Easily Track Daily and Weekly tasks... in real life", + Description = "We were just informed there are these things called \"chores\" outside the game. No worries, though, we can track them!", Author = "MidoriKami", RealAuthor = "Berna", Type = typeof(DailyLifeDutyPlugin), @@ -144,7 +144,6 @@ internal class FoolsManager : IDisposable, IServiceType RealAuthor = "Berna", Type = typeof(YesHealMePlugin), }, - }; } From b91ce2c46fb10591d5e58cbf5f1c993322dbaa3b Mon Sep 17 00:00:00 2001 From: Bernardo Lopes Date: Mon, 20 Mar 2023 11:15:13 -0300 Subject: [PATCH 21/34] [Fools-YesHealMePlugin] Finished implementation. --- Dalamud/Fools/FoolsManager.cs | 2 +- Dalamud/Fools/Helper/YesHealMe/Colors.cs | 31 ++++++ .../Fools/Helper/YesHealMe/DrawUtilities.cs | 99 +++++++++++++++++++ Dalamud/Fools/Helper/YesHealMe/FontManager.cs | 20 ++++ Dalamud/Fools/Helper/YesHealMe/IconCache.cs | 70 +++++++++++++ Dalamud/Fools/Plugins/YesHealMePlugin.cs | 35 ++++--- .../Fools/Plugins/YesHealMePluginWindow.cs | 97 ++++++++++++++++++ 7 files changed, 335 insertions(+), 19 deletions(-) create mode 100644 Dalamud/Fools/Helper/YesHealMe/Colors.cs create mode 100644 Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs create mode 100644 Dalamud/Fools/Helper/YesHealMe/FontManager.cs create mode 100644 Dalamud/Fools/Helper/YesHealMe/IconCache.cs create mode 100644 Dalamud/Fools/Plugins/YesHealMePluginWindow.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index d81d1dbe6..761dd0c08 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -139,7 +139,7 @@ internal class FoolsManager : IDisposable, IServiceType { Name = "YesHealMe", InternalName = "YesHealMePlugin", - Description = "The only warning you need.", + Description = "As the saying goes: it's the first missing HP that matters. And the second. And the third...", Author = "MidoriKami", RealAuthor = "Berna", Type = typeof(YesHealMePlugin), diff --git a/Dalamud/Fools/Helper/YesHealMe/Colors.cs b/Dalamud/Fools/Helper/YesHealMe/Colors.cs new file mode 100644 index 000000000..9828fe54b --- /dev/null +++ b/Dalamud/Fools/Helper/YesHealMe/Colors.cs @@ -0,0 +1,31 @@ +using System.Numerics; +using ImGuiNET; + +namespace Dalamud.Fools.Helper.YesHealMe; + +public static class Colors +{ + public static Vector4 Purple = new(176 / 255.0f, 38 / 255.0f, 236 / 255.0f, 1.0f); + public static Vector4 Blue = new(37 / 255.0f, 168 / 255.0f, 1.0f, 1.0f); + public static Vector4 ForestGreen = new(0.133f, 0.545f, 0.1333f, 1.0f); + public static Vector4 White = new(1.0f, 1.0f, 1.0f,1.0f); + public static Vector4 Red = new(1.0f, 0.0f, 0.0f, 1.0f); + public static Vector4 Green = new(0.0f, 1.0f, 0.0f, 1.0f); + public static Vector4 Black = new(0.0f, 0.0f, 0.0f, 1.0f); + public static Vector4 HealerGreen = new(33 / 255f, 193 / 255f, 0, 1.0f); + public static Vector4 DPSRed = new(210/255f, 42/255f, 43/255f, 1.0f); + public static Vector4 SoftRed = new(0.8f, 0.2f, 0.2f, 1.0f); + public static Vector4 Grey = new(0.6f, 0.6f, 0.6f, 1.0f); + public static Vector4 LightGrey = new(220/250.0f, 220/250.0f, 220/250f, 1.0f); + public static Vector4 Orange = new(1.0f, 165.0f / 255.0f, 0.0f, 1.0f); + public static Vector4 SoftGreen = new(0.2f, 0.8f, 0.2f, 1.0f); + public static Vector4 FatePink = new(0.58f, 0.388f, 0.827f, 0.33f); + public static Vector4 FateDarkPink = new(0.58f, 0.388f, 0.827f, 1.0f); + public static Vector4 MapTextBrown = new(0.655f, 0.396f, 0.149f, 1.0f); + public static Vector4 BabyBlue = new(0.537f, 0.812f, 0.941f, 1.0f); +} + +public static class ColorExtensions +{ + public static uint ToU32(this Vector4 color) => ImGui.GetColorU32(color); +} diff --git a/Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs b/Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs new file mode 100644 index 000000000..38bc401ad --- /dev/null +++ b/Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs @@ -0,0 +1,99 @@ +using System; +using System.Numerics; +using ImGuiNET; +using KamiLib.Caching; + +namespace Dalamud.Fools.Helper.YesHealMe; + +internal static class DrawUtilities +{ + public static void TextOutlined(FontManager fontManager, Vector2 startingPosition, string text, float scale, Vector4 color) + { + startingPosition = startingPosition.Ceil(); + + var outlineThickness = (int)MathF.Ceiling(1 * scale); + + for (var x = -outlineThickness; x <= outlineThickness; ++x) + { + for (var y = -outlineThickness; y <= outlineThickness; ++y) + { + if (x == 0 && y == 0) + { + continue; + } + + DrawText(fontManager, startingPosition + new Vector2(x, y), text, Colors.Black, scale); + } + } + + DrawText(fontManager, startingPosition, text, color, scale); + } + + public static void DrawIconWithName(FontManager fontManager, Vector2 drawPosition, uint iconID, string name, float iconScale, float textScale, bool drawText = true) + { + if (!fontManager.GameFont.Available) return; + + var icon = IconCache.Instance.GetIcon(iconID); + if (icon != null) + { + var drawList = ImGui.GetBackgroundDrawList(); + + var imagePadding = new Vector2(20.0f, 10.0f) * iconScale; + var imageSize = new Vector2(50.0f, 50.0f) * iconScale; + + drawPosition += imagePadding; + + drawList.AddImage(icon.ImGuiHandle, drawPosition, drawPosition + imageSize); + + if (drawText) + { + drawPosition.X += imageSize.X / 2.0f; + drawPosition.Y += imageSize.Y + 2.0f * iconScale; + + var textSize = CalculateTextSize(fontManager, name, textScale / 2.75f); + var textOffset = new Vector2(0.0f, 5.0f) * iconScale; + + drawPosition.X -= textSize.X / 2.0f; + + TextOutlined(fontManager, drawPosition + textOffset, name, textScale / 2.75f, Colors.White); + } + } + } + + public static Vector2 CalculateTextSize(FontManager fontManager, string text, float scale) + { + if(!fontManager.GameFont.Available) return Vector2.Zero; + + var fontSize = fontManager.GameFont.ImFont.FontSize; + var textSize = ImGui.CalcTextSize(text); + var fontScalar = 62.0f / textSize.Y; + + var textWidth = textSize.X * fontScalar; + + return new Vector2(textWidth, fontSize) * scale; + } + + private static void DrawText(FontManager fontManager, Vector2 drawPosition, string text, Vector4 color, float scale, bool debug = false) + { + if (!fontManager.GameFont.Available) return; + var font = fontManager.GameFont.ImFont; + + var drawList = ImGui.GetBackgroundDrawList(); + var stringSize = CalculateTextSize(fontManager, text, scale); + + if (debug) + { + drawList.AddRect(drawPosition, drawPosition + stringSize, ImGui.GetColorU32(Colors.Green)); + } + + drawList.AddText(font, font.FontSize * scale, drawPosition, ImGui.GetColorU32(color), text); + } +} + +public static class VectorExtensions +{ + public static Vector2 Ceil(this Vector2 data) + { + return new Vector2(MathF.Ceiling(data.X), MathF.Ceiling(data.Y)); + } +} diff --git a/Dalamud/Fools/Helper/YesHealMe/FontManager.cs b/Dalamud/Fools/Helper/YesHealMe/FontManager.cs new file mode 100644 index 000000000..d7ea88fd0 --- /dev/null +++ b/Dalamud/Fools/Helper/YesHealMe/FontManager.cs @@ -0,0 +1,20 @@ +using System; +using Dalamud.Interface; +using Dalamud.Interface.GameFonts; + +namespace Dalamud.Fools.Helper.YesHealMe; + +public class FontManager : IDisposable +{ + public GameFontHandle GameFont { get; } + + public FontManager(UiBuilder uiBuilder) + { + this.GameFont = uiBuilder.GetGameFontHandle( new GameFontStyle( GameFontFamily.Axis, 52.0f ) ); + } + + public void Dispose() + { + this.GameFont.Dispose(); + } +} diff --git a/Dalamud/Fools/Helper/YesHealMe/IconCache.cs b/Dalamud/Fools/Helper/YesHealMe/IconCache.cs new file mode 100644 index 000000000..0a64d030e --- /dev/null +++ b/Dalamud/Fools/Helper/YesHealMe/IconCache.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Dalamud; +using Dalamud.Data; +using Dalamud.Logging; +using Dalamud.Utility; +using ImGuiScene; + +namespace KamiLib.Caching; + +public class IconCache : IDisposable +{ + private readonly Dictionary iconTextures = new(); + + private const string IconFilePath = "ui/icon/{0:D3}000/{1:D6}_hr1.tex"; + + private static IconCache? _instance; + public static IconCache Instance => _instance ??= new IconCache(); + + public static void Cleanup() + { + _instance?.Dispose(); + } + + public void Dispose() + { + foreach (var texture in iconTextures.Values) + { + texture?.Dispose(); + } + + iconTextures.Clear(); + } + + private void LoadIconTexture(uint iconId) + { + Task.Run(() => + { + try + { + var path = IconFilePath.Format(iconId / 1000, iconId); + var tex = Service.Get().GetImGuiTexture(path); + + if (tex is not null && tex.ImGuiHandle != nint.Zero) + { + iconTextures[iconId] = tex; + } + else + { + tex?.Dispose(); + } + } + catch (Exception ex) + { + PluginLog.LogError($"Failed loading texture for icon {iconId} - {ex.Message}"); + } + }); + } + + public TextureWrap? GetIcon(uint iconId) + { + if (iconTextures.TryGetValue(iconId, out var value)) return value; + + iconTextures.Add(iconId, null); + LoadIconTexture(iconId); + + return iconTextures[iconId]; + } +} diff --git a/Dalamud/Fools/Plugins/YesHealMePlugin.cs b/Dalamud/Fools/Plugins/YesHealMePlugin.cs index 5b416b0a6..b33d24e0e 100644 --- a/Dalamud/Fools/Plugins/YesHealMePlugin.cs +++ b/Dalamud/Fools/Plugins/YesHealMePlugin.cs @@ -1,32 +1,31 @@ -using System.Linq; -using Dalamud.Logging; +using Dalamud.Fools.Helper.YesHealMe; +using Dalamud.Interface; using NoTankYou.System; namespace Dalamud.Fools.Plugins; -public class YesHealMePlugin: IFoolsPlugin +public class YesHealMePlugin : IFoolsPlugin { - private PartyListAddon partyListAddon; + private readonly FontManager fontManager; + private readonly PartyListAddon partyListAddon = new(); + private int iconId = 1; public YesHealMePlugin() { - partyListAddon = new PartyListAddon(); + const string nameSpace = "fools+YesHealMe"; + var uiBuilder = new UiBuilder(nameSpace); + this.fontManager = new FontManager(uiBuilder); + } + + /// + public void Dispose() + { + this.fontManager.Dispose(); + this.partyListAddon.Dispose(); } public void DrawUi() { - foreach (var partyMember in this.partyListAddon.Select(pla => pla.PlayerCharacter).Where(pc => pc is not null)) - { - if (partyMember.CurrentHp < partyMember.MaxHp) - { - // Do things here - } - } - } - - - public void Dispose() - { - this.partyListAddon.Dispose(); + YesHealMePluginWindow.Draw(this.partyListAddon, this.fontManager, ref this.iconId); } } diff --git a/Dalamud/Fools/Plugins/YesHealMePluginWindow.cs b/Dalamud/Fools/Plugins/YesHealMePluginWindow.cs new file mode 100644 index 000000000..36dfc2a44 --- /dev/null +++ b/Dalamud/Fools/Plugins/YesHealMePluginWindow.cs @@ -0,0 +1,97 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Dalamud.Data; +using Dalamud.Fools.Helper.YesHealMe; +using Dalamud.Game.ClientState; +using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using Dalamud.Interface.Components; +using Dalamud.Interface.Internal; +using ImGuiNET; +using NoTankYou.System; + +namespace Dalamud.Fools.Plugins; + +public static class YesHealMePluginWindow +{ + private const string WindowName = "##foolsYesHealMeBanner"; + private const string WarningText = "HEAL ME"; + private const int Length = 300; + private const int SectionHeight = 100; + private const float Scale = 1f; + private static readonly Vector2 Position = new(200, 200); + private static readonly Vector2 Size = new(Length, SectionHeight); + + private static IEnumerable Characters(PartyListAddon partyListAddon) + { + return partyListAddon.Any() ? partyListAddon.Select(pla => pla.PlayerCharacter) : new[] { Service.Get().LocalPlayer }; + } + + private static List HurtingCharacters(IEnumerable characters) + { + return characters + .Where(pc => pc.CurrentHp < pc.MaxHp || + Service.Get() + .IsDevMenuOpen) + .ToList(); + } + + public static void Draw(PartyListAddon partyListAddon, FontManager fontManager, ref int iconId) + { + ImGui.SetNextWindowPos(Position, ImGuiCond.FirstUseEver); + ImGui.SetNextWindowSize(Size); + ImGui.SetNextWindowSizeConstraints(Size, Size); + ImGui.Begin(WindowName, ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoBackground); + var playersDrawn = 0; + var hurtingCharacters = HurtingCharacters(Characters(partyListAddon)); + if (hurtingCharacters.Count > 0) + { + var windowPos = ImGui.GetCursorScreenPos(); + foreach (var hurtingCharacter in hurtingCharacters) + { + var position = windowPos + new Vector2(0, playersDrawn * SectionHeight); + var healMeTextSize = DrawUtilities.CalculateTextSize(fontManager, WarningText, Scale); + var healMePosition = position with + { + X = position.X + healMeTextSize.X, + }; + DrawHealMeText(fontManager, position); + DrawPlayerName(fontManager, hurtingCharacter, healMePosition); + DrawIcon(fontManager, healMePosition); + playersDrawn += 1; + } + } + + ImGui.End(); + } + + private static void DrawIcon(FontManager fontManager, Vector2 healMePosition) + { + DrawUtilities.DrawIconWithName(fontManager, healMePosition, 62582, "pls? owo", 1, 1); + } + + private static void DrawHealMeText(FontManager fontManager, Vector2 position) + { + // HEAL ME text + DrawUtilities.TextOutlined(fontManager, position, WarningText, 1, Colors.White); + } + + private static void DrawPlayerName( + FontManager fontManager, PlayerCharacter hurtingCharacter, Vector2 healMePosition) + { + var textSize = DrawUtilities.CalculateTextSize(fontManager, hurtingCharacter.Name.TextValue, Scale); + var namePosition = new Vector2 + { + X = healMePosition.X - textSize.X / 2.0f, + Y = healMePosition.Y + textSize.Y, + }; + DrawUtilities.TextOutlined( + fontManager, + namePosition, + hurtingCharacter.Name.TextValue, + Scale / 2f, + Colors.White); + } +} From 81aacb56ee06c8500a2a183061a8a16d2b8d572d Mon Sep 17 00:00:00 2001 From: NotNite Date: Sat, 25 Mar 2023 15:50:53 -0400 Subject: [PATCH 22/34] Add Complicated Tweaks --- Dalamud/Fools/FoolsManager.cs | 9 +++ .../Fools/Plugins/ComplicatedTweaksPlugin.cs | 68 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 Dalamud/Fools/Plugins/ComplicatedTweaksPlugin.cs diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 761dd0c08..a6ed3726f 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -144,6 +144,15 @@ internal class FoolsManager : IDisposable, IServiceType RealAuthor = "Berna", Type = typeof(YesHealMePlugin), }, + new() + { + Name = "Complicated Tweaks", + InternalName = "ComplicatedTweaksPlugin", + Description = "As complicated as it gets!", + Author = "Caraxi", + RealAuthor = "NotNite", + Type = typeof(ComplicatedTweaksPlugin), + }, }; } diff --git a/Dalamud/Fools/Plugins/ComplicatedTweaksPlugin.cs b/Dalamud/Fools/Plugins/ComplicatedTweaksPlugin.cs new file mode 100644 index 000000000..6e9e19aae --- /dev/null +++ b/Dalamud/Fools/Plugins/ComplicatedTweaksPlugin.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ImGuiNET; + +namespace Dalamud.Fools.Plugins; + +public class ComplicatedTweaksPlugin : IFoolsPlugin +{ + enum Widget + { + Button, + Checkbox, + DragFloat, + InputFloat, + } + + private List widgets; + + public ComplicatedTweaksPlugin() + { + this.widgets = new List(); + + var random = new Random(); + var possibleWidgets = Enum.GetValues(typeof(Widget)).Cast().ToList(); + + for (var i = 0; i < 100; i++) + { + var widget = possibleWidgets[random.Next(possibleWidgets.Count)]; + this.widgets.Add(widget); + } + } + + public void DrawUi() + { + if (ImGui.Begin("Complicated Tweaks")) + { + foreach (var widget in this.widgets) + { + switch (widget) + { + case Widget.Button: + ImGui.Button("Click me!"); + break; + + case Widget.Checkbox: + var iHateImgui = false; + ImGui.Checkbox(string.Empty, ref iHateImgui); + break; + + case Widget.DragFloat: + var dragFloat = 0f; + ImGui.DragFloat(string.Empty, ref dragFloat); + break; + + case Widget.InputFloat: + var inputFloat = 0f; + ImGui.InputFloat(string.Empty, ref inputFloat); + break; + } + } + } + + ImGui.End(); + } + + public void Dispose() { } +} From b77bf380f2849fdb1f3656702640fb1798736af3 Mon Sep 17 00:00:00 2001 From: Bernardo Lopes Date: Mon, 27 Mar 2023 07:33:41 -0300 Subject: [PATCH 23/34] [Fools-YesHealMePlugin] Removed unused parts in the helper functions. --- Dalamud/Fools/Helper/YesHealMe/Colors.cs | 26 +------- .../Fools/Helper/YesHealMe/DrawUtilities.cs | 62 +++++++++--------- Dalamud/Fools/Helper/YesHealMe/FontManager.cs | 6 +- Dalamud/Fools/Helper/YesHealMe/HudHelper.cs | 20 +++--- Dalamud/Fools/Helper/YesHealMe/IconCache.cs | 63 ++++++++++--------- .../Fools/Helper/YesHealMe/PartyListAddon.cs | 53 +++++++--------- .../Helper/YesHealMe/PartyListAddonData.cs | 35 ++--------- Dalamud/Fools/Plugins/YesHealMePlugin.cs | 2 +- .../Fools/Plugins/YesHealMePluginWindow.cs | 7 +-- 9 files changed, 104 insertions(+), 170 deletions(-) diff --git a/Dalamud/Fools/Helper/YesHealMe/Colors.cs b/Dalamud/Fools/Helper/YesHealMe/Colors.cs index 9828fe54b..ab2473199 100644 --- a/Dalamud/Fools/Helper/YesHealMe/Colors.cs +++ b/Dalamud/Fools/Helper/YesHealMe/Colors.cs @@ -1,31 +1,9 @@ using System.Numerics; -using ImGuiNET; namespace Dalamud.Fools.Helper.YesHealMe; public static class Colors { - public static Vector4 Purple = new(176 / 255.0f, 38 / 255.0f, 236 / 255.0f, 1.0f); - public static Vector4 Blue = new(37 / 255.0f, 168 / 255.0f, 1.0f, 1.0f); - public static Vector4 ForestGreen = new(0.133f, 0.545f, 0.1333f, 1.0f); - public static Vector4 White = new(1.0f, 1.0f, 1.0f,1.0f); - public static Vector4 Red = new(1.0f, 0.0f, 0.0f, 1.0f); - public static Vector4 Green = new(0.0f, 1.0f, 0.0f, 1.0f); - public static Vector4 Black = new(0.0f, 0.0f, 0.0f, 1.0f); - public static Vector4 HealerGreen = new(33 / 255f, 193 / 255f, 0, 1.0f); - public static Vector4 DPSRed = new(210/255f, 42/255f, 43/255f, 1.0f); - public static Vector4 SoftRed = new(0.8f, 0.2f, 0.2f, 1.0f); - public static Vector4 Grey = new(0.6f, 0.6f, 0.6f, 1.0f); - public static Vector4 LightGrey = new(220/250.0f, 220/250.0f, 220/250f, 1.0f); - public static Vector4 Orange = new(1.0f, 165.0f / 255.0f, 0.0f, 1.0f); - public static Vector4 SoftGreen = new(0.2f, 0.8f, 0.2f, 1.0f); - public static Vector4 FatePink = new(0.58f, 0.388f, 0.827f, 0.33f); - public static Vector4 FateDarkPink = new(0.58f, 0.388f, 0.827f, 1.0f); - public static Vector4 MapTextBrown = new(0.655f, 0.396f, 0.149f, 1.0f); - public static Vector4 BabyBlue = new(0.537f, 0.812f, 0.941f, 1.0f); -} - -public static class ColorExtensions -{ - public static uint ToU32(this Vector4 color) => ImGui.GetColorU32(color); + public static readonly Vector4 White = new(1.0f, 1.0f, 1.0f, 1.0f); + public static readonly Vector4 Black = new(0.0f, 0.0f, 0.0f, 1.0f); } diff --git a/Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs b/Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs index 38bc401ad..b9a571d9c 100644 --- a/Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs +++ b/Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs @@ -1,13 +1,13 @@ using System; using System.Numerics; using ImGuiNET; -using KamiLib.Caching; namespace Dalamud.Fools.Helper.YesHealMe; internal static class DrawUtilities { - public static void TextOutlined(FontManager fontManager, Vector2 startingPosition, string text, float scale, Vector4 color) + public static void TextOutlined( + FontManager fontManager, Vector2 startingPosition, string text, float scale, Vector4 color) { startingPosition = startingPosition.Ceil(); @@ -29,40 +29,44 @@ internal static class DrawUtilities DrawText(fontManager, startingPosition, text, color, scale); } - public static void DrawIconWithName(FontManager fontManager, Vector2 drawPosition, uint iconID, string name, float iconScale, float textScale, bool drawText = true) + public static void DrawIconWithName( + FontManager fontManager, Vector2 drawPosition, uint iconID, string name, float iconScale, float textScale) { - if (!fontManager.GameFont.Available) return; - + if (!fontManager.GameFont.Available) + { + return; + } + var icon = IconCache.Instance.GetIcon(iconID); if (icon != null) { var drawList = ImGui.GetBackgroundDrawList(); - + var imagePadding = new Vector2(20.0f, 10.0f) * iconScale; var imageSize = new Vector2(50.0f, 50.0f) * iconScale; - + drawPosition += imagePadding; - + drawList.AddImage(icon.ImGuiHandle, drawPosition, drawPosition + imageSize); - - if (drawText) - { - drawPosition.X += imageSize.X / 2.0f; - drawPosition.Y += imageSize.Y + 2.0f * iconScale; - - var textSize = CalculateTextSize(fontManager, name, textScale / 2.75f); - var textOffset = new Vector2(0.0f, 5.0f) * iconScale; - - drawPosition.X -= textSize.X / 2.0f; - - TextOutlined(fontManager, drawPosition + textOffset, name, textScale / 2.75f, Colors.White); - } + + drawPosition.X += imageSize.X / 2.0f; + drawPosition.Y += imageSize.Y + (2.0f * iconScale); + + var textSize = CalculateTextSize(fontManager, name, textScale / 2.75f); + var textOffset = new Vector2(0.0f, 5.0f) * iconScale; + + drawPosition.X -= textSize.X / 2.0f; + + TextOutlined(fontManager, drawPosition + textOffset, name, textScale / 2.75f, Colors.White); } } public static Vector2 CalculateTextSize(FontManager fontManager, string text, float scale) { - if(!fontManager.GameFont.Available) return Vector2.Zero; + if (!fontManager.GameFont.Available) + { + return Vector2.Zero; + } var fontSize = fontManager.GameFont.ImFont.FontSize; var textSize = ImGui.CalcTextSize(text); @@ -73,18 +77,16 @@ internal static class DrawUtilities return new Vector2(textWidth, fontSize) * scale; } - private static void DrawText(FontManager fontManager, Vector2 drawPosition, string text, Vector4 color, float scale, bool debug = false) + private static void DrawText(FontManager fontManager, Vector2 drawPosition, string text, Vector4 color, float scale) { - if (!fontManager.GameFont.Available) return; + if (!fontManager.GameFont.Available) + { + return; + } + var font = fontManager.GameFont.ImFont; var drawList = ImGui.GetBackgroundDrawList(); - var stringSize = CalculateTextSize(fontManager, text, scale); - - if (debug) - { - drawList.AddRect(drawPosition, drawPosition + stringSize, ImGui.GetColorU32(Colors.Green)); - } drawList.AddText(font, font.FontSize * scale, drawPosition, ImGui.GetColorU32(color), text); } diff --git a/Dalamud/Fools/Helper/YesHealMe/FontManager.cs b/Dalamud/Fools/Helper/YesHealMe/FontManager.cs index d7ea88fd0..7c7244ba9 100644 --- a/Dalamud/Fools/Helper/YesHealMe/FontManager.cs +++ b/Dalamud/Fools/Helper/YesHealMe/FontManager.cs @@ -6,13 +6,13 @@ namespace Dalamud.Fools.Helper.YesHealMe; public class FontManager : IDisposable { - public GameFontHandle GameFont { get; } - public FontManager(UiBuilder uiBuilder) { - this.GameFont = uiBuilder.GetGameFontHandle( new GameFontStyle( GameFontFamily.Axis, 52.0f ) ); + this.GameFont = uiBuilder.GetGameFontHandle(new GameFontStyle(GameFontFamily.Axis, 52.0f)); } + public GameFontHandle GameFont { get; } + public void Dispose() { this.GameFont.Dispose(); diff --git a/Dalamud/Fools/Helper/YesHealMe/HudHelper.cs b/Dalamud/Fools/Helper/YesHealMe/HudHelper.cs index 6de258c6e..1c11ee84c 100644 --- a/Dalamud/Fools/Helper/YesHealMe/HudHelper.cs +++ b/Dalamud/Fools/Helper/YesHealMe/HudHelper.cs @@ -1,40 +1,34 @@ -using Dalamud; -using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; using FFXIVClientStructs.FFXIV.Client.UI.Agent; -namespace NoTankYou.Utilities; +namespace Dalamud.Fools.Helper.YesHealMe; public static unsafe class HudHelper { private static AgentHUD* AgentHud => AgentModule.Instance()->GetAgentHUD(); - + public static PlayerCharacter? GetPlayerCharacter(int index) { // Sorta temporary, waiting for ClientStructs to merge a fixed size array for this element var partyMemberList = AgentHud->PartyMemberList; var targetOffset = index * sizeof(HudPartyMember); var targetAddress = partyMemberList + targetOffset; - var hudData = (HudPartyMember*) targetAddress; - + var hudData = (HudPartyMember*)targetAddress; + var targetPlayer = hudData->ObjectId; return GetPlayer(targetPlayer); } - - public static PlayerCharacter? GetAllianceMember(int index) - { - var targetPlayer = AgentHud->RaidMemberIds[index]; - - return GetPlayer(targetPlayer); - } private static PlayerCharacter? GetPlayer(uint objectId) { var result = Service.Get().SearchById(objectId); if (result?.GetType() == typeof(PlayerCharacter)) + { return result as PlayerCharacter; + } return null; } diff --git a/Dalamud/Fools/Helper/YesHealMe/IconCache.cs b/Dalamud/Fools/Helper/YesHealMe/IconCache.cs index 0a64d030e..6f82b0d7f 100644 --- a/Dalamud/Fools/Helper/YesHealMe/IconCache.cs +++ b/Dalamud/Fools/Helper/YesHealMe/IconCache.cs @@ -1,70 +1,71 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Dalamud; using Dalamud.Data; using Dalamud.Logging; using Dalamud.Utility; using ImGuiScene; -namespace KamiLib.Caching; +namespace Dalamud.Fools.Helper.YesHealMe; public class IconCache : IDisposable { - private readonly Dictionary iconTextures = new(); - private const string IconFilePath = "ui/icon/{0:D3}000/{1:D6}_hr1.tex"; - - private static IconCache? _instance; - public static IconCache Instance => _instance ??= new IconCache(); - - public static void Cleanup() + + private static IconCache? internalInstance; + private readonly Dictionary iconTextures = new(); + public static IconCache Instance => internalInstance ??= new IconCache(); + + public void Dispose() { - _instance?.Dispose(); - } - - public void Dispose() - { - foreach (var texture in iconTextures.Values) + foreach (var texture in this.iconTextures.Values) { texture?.Dispose(); } - iconTextures.Clear(); + this.iconTextures.Clear(); } - - private void LoadIconTexture(uint iconId) + + public static void Cleanup() { - Task.Run(() => + internalInstance?.Dispose(); + } + + private void LoadIconTexture(uint iconId) + { + Task.Run(() => { try { var path = IconFilePath.Format(iconId / 1000, iconId); var tex = Service.Get().GetImGuiTexture(path); - if (tex is not null && tex.ImGuiHandle != nint.Zero) + if (tex is not null && tex.ImGuiHandle != nint.Zero) { - iconTextures[iconId] = tex; - } - else + this.iconTextures[iconId] = tex; + } + else { tex?.Dispose(); } - } - catch (Exception ex) + } + catch (Exception ex) { PluginLog.LogError($"Failed loading texture for icon {iconId} - {ex.Message}"); } }); } - - public TextureWrap? GetIcon(uint iconId) + + public TextureWrap? GetIcon(uint iconId) { - if (iconTextures.TryGetValue(iconId, out var value)) return value; + if (this.iconTextures.TryGetValue(iconId, out var value)) + { + return value; + } - iconTextures.Add(iconId, null); - LoadIconTexture(iconId); + this.iconTextures.Add(iconId, null); + this.LoadIconTexture(iconId); - return iconTextures[iconId]; + return this.iconTextures[iconId]; } } diff --git a/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs b/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs index a2b106b40..29af9e483 100644 --- a/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs +++ b/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs @@ -2,66 +2,57 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Numerics; -using Dalamud; using Dalamud.Game; using Dalamud.Game.Gui; using FFXIVClientStructs.FFXIV.Client.UI; -using NoTankYou.DataModels; -using NoTankYou.Utilities; -namespace NoTankYou.System; +namespace Dalamud.Fools.Helper.YesHealMe; public unsafe class PartyListAddon : IEnumerable, IDisposable { - public record PartyFramePositionInfo(Vector2 Position, Vector2 Size, Vector2 Scale); - public IEnumerator GetEnumerator() => addonData.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - private static AddonPartyList* PartyList => (AddonPartyList*) Service.Get()?.GetAddonByName("_PartyList"); - public static bool DataAvailable => PartyList != null && PartyList->AtkUnitBase.RootNode != null; - private readonly List addonData = new(); public PartyListAddon() { - Service.Get().Update += OnFrameworkUpdate; + Service.Get().Update += this.OnFrameworkUpdate; } + private static AddonPartyList* PartyList => (AddonPartyList*)Service.Get()?.GetAddonByName("_PartyList"); + private static bool DataAvailable => PartyList != null && PartyList->AtkUnitBase.RootNode != null; + public void Dispose() { - Service.Get().Update -= OnFrameworkUpdate; + Service.Get().Update -= this.OnFrameworkUpdate; + } + + public IEnumerator GetEnumerator() + { + return this.addonData.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); } private void OnFrameworkUpdate(Framework framework) { - addonData.Clear(); - if (!DataAvailable) return; - if (PartyList->MemberCount <= 0) return; + this.addonData.Clear(); + if (!DataAvailable || PartyList->MemberCount <= 0) + { + return; + } foreach (var index in Enumerable.Range(0, PartyList->MemberCount)) { var playerCharacter = HudHelper.GetPlayerCharacter(index); var userInterface = PartyList->PartyMember[index]; - addonData.Add(new PartyListAddonData + this.addonData.Add(new PartyListAddonData { PlayerCharacter = playerCharacter, UserInterface = userInterface, }); } } - - public static PartyFramePositionInfo GetPositionInfo() - { - // Resource Node (id 9) contains a weird offset for the actual list elements - var rootNode = PartyList->AtkUnitBase.RootNode; - var addonBasePosition = new Vector2(rootNode->X, rootNode->Y); - var scale = new Vector2(rootNode->ScaleX, rootNode->ScaleY); - - var partyListNode = PartyList->AtkUnitBase.GetNodeById(9); - var partyListPositionOffset = new Vector2(partyListNode->X, partyListNode->Y) * scale; - var partyListSize = new Vector2(partyListNode->Width, partyListNode->Height); - - return new PartyFramePositionInfo(addonBasePosition + partyListPositionOffset, partyListSize * scale, scale); - } } diff --git a/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs b/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs index b9989ab0b..0dbdd3913 100644 --- a/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs +++ b/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs @@ -1,37 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.ClientState.Objects.SubKinds; using FFXIVClientStructs.FFXIV.Client.UI; -namespace NoTankYou.DataModels; +namespace Dalamud.Fools.Helper.YesHealMe; -public readonly unsafe struct PartyListAddonData +public readonly struct PartyListAddonData { - private static readonly Dictionary TimeSinceLastTargetable = new(); - public AddonPartyList.PartyListMemberStruct UserInterface { get; init; } public PlayerCharacter? PlayerCharacter { get; init; } - - private bool Targetable => UserInterface.PartyMemberComponent->OwnerNode->AtkResNode.Color.A != 0x99; - - public bool IsTargetable() - { - if (PlayerCharacter is null) return false; - - TimeSinceLastTargetable.TryAdd(PlayerCharacter.ObjectId, Stopwatch.StartNew()); - var stopwatch = TimeSinceLastTargetable[PlayerCharacter.ObjectId]; - - if (Targetable) - { - // Returns true if the party member has been targetable for 2second or more - return stopwatch.Elapsed >= TimeSpan.FromSeconds(2); - } - else - { - // Returns false, and continually resets the stopwatch - stopwatch.Restart(); - return false; - } - } -} \ No newline at end of file +} diff --git a/Dalamud/Fools/Plugins/YesHealMePlugin.cs b/Dalamud/Fools/Plugins/YesHealMePlugin.cs index b33d24e0e..3e2b2c213 100644 --- a/Dalamud/Fools/Plugins/YesHealMePlugin.cs +++ b/Dalamud/Fools/Plugins/YesHealMePlugin.cs @@ -1,6 +1,5 @@ using Dalamud.Fools.Helper.YesHealMe; using Dalamud.Interface; -using NoTankYou.System; namespace Dalamud.Fools.Plugins; @@ -22,6 +21,7 @@ public class YesHealMePlugin : IFoolsPlugin { this.fontManager.Dispose(); this.partyListAddon.Dispose(); + IconCache.Cleanup(); } public void DrawUi() diff --git a/Dalamud/Fools/Plugins/YesHealMePluginWindow.cs b/Dalamud/Fools/Plugins/YesHealMePluginWindow.cs index 36dfc2a44..4bb30ab0d 100644 --- a/Dalamud/Fools/Plugins/YesHealMePluginWindow.cs +++ b/Dalamud/Fools/Plugins/YesHealMePluginWindow.cs @@ -1,16 +1,11 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using Dalamud.Data; using Dalamud.Fools.Helper.YesHealMe; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Game.Text.SeStringHandling.Payloads; -using Dalamud.Interface.Components; using Dalamud.Interface.Internal; using ImGuiNET; -using NoTankYou.System; namespace Dalamud.Fools.Plugins; @@ -84,7 +79,7 @@ public static class YesHealMePluginWindow var textSize = DrawUtilities.CalculateTextSize(fontManager, hurtingCharacter.Name.TextValue, Scale); var namePosition = new Vector2 { - X = healMePosition.X - textSize.X / 2.0f, + X = healMePosition.X - (textSize.X / 2.0f), Y = healMePosition.Y + textSize.Y, }; DrawUtilities.TextOutlined( From 3272bfba71037490c3a8b7498ae05d428b35c367 Mon Sep 17 00:00:00 2001 From: chirp Date: Tue, 28 Mar 2023 16:37:14 +0100 Subject: [PATCH 24/34] Update FoolsManager.cs --- Dalamud/Fools/FoolsManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index a6ed3726f..bd9fa7903 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -92,7 +92,7 @@ internal class FoolsManager : IDisposable, IServiceType }, new() { - Name = "Oops, Maybe Lalafells!", + Name = "Oops, Maybe Lalafells?", InternalName = "OopsMaybeLalafellsPlugin", Description = "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.", Author = "Chirpopo Chirpo", From 8820b067b8043e876a6a0fd8cda452735e95c2d2 Mon Sep 17 00:00:00 2001 From: Ava Chaney Date: Tue, 28 Mar 2023 21:31:00 -0700 Subject: [PATCH 25/34] implement Allagan Voice Recognition Technology into Dalamud --- Dalamud/Dalamud.csproj | 1 + Dalamud/Fools/FoolsManager.cs | 10 +- Dalamud/Fools/Plugins/HeyDalamudPlugin.cs | 278 ++++++++++++++++++++++ 3 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 Dalamud/Fools/Plugins/HeyDalamudPlugin.cs diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index beb14c61e..8310c7047 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -85,6 +85,7 @@ + diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index bd9fa7903..336aed539 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -153,10 +153,18 @@ internal class FoolsManager : IDisposable, IServiceType RealAuthor = "NotNite", Type = typeof(ComplicatedTweaksPlugin), }, + new() + { + Name = "Hey Dalamud!", + InternalName = "HeyDalamudPlugin", + Description = "Scientists have unearthed advanced Allagan Voice Recognition Technology from before the Calamity, then they used it in a Dalamud plugin. Was it a good idea? That's for you to decide.", + Author = "snake", + RealAuthor = "Avaflow", + Type = typeof(HeyDalamudPlugin), + }, }; } - public void ActivatePlugin(string plugin) { if (this.ActivatedPlugins.ContainsKey(plugin)) diff --git a/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs b/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs new file mode 100644 index 000000000..ae3efd716 --- /dev/null +++ b/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Net.Http; +using System.Runtime.InteropServices; +using System.Speech.Recognition; +using System.Speech.Synthesis; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Dalamud.Data; +using Dalamud.Fools.Helper; +using Dalamud.Game.ClientState; +using Dalamud.Game.Command; +using Dalamud.Logging.Internal; +using Dalamud.Utility; +using FFXIVClientStructs.FFXIV.Client.UI; +using Lumina.Excel.GeneratedSheets; +using Newtonsoft.Json; + +namespace Dalamud.Fools.Plugins; + +public class HeyDalamudPlugin : IFoolsPlugin +{ + public enum DetectionState + { + Hotword, + Action + } + + private const uint StartSoundId = 23; + private const uint ErrorSoundId = 24; + private const uint StopSoundId = 31; + + private const string ApiEndpoint = "https://allagan-voice-terminal.goat.place"; + + private static readonly string PluginName = "Hey, Dalamud!"; + private static readonly ModuleLog Log = new("FOOLS-HD"); + + private readonly CancellationTokenSource noComprehendTaskCancellationTokenSource = new(); + + private Grammar actionGrammar; + + private readonly ClientState clientState; + private readonly DataManager dataManager; + private readonly CommandManager commandManager; + + private readonly CultureInfo definedCulture = CultureInfo.GetCultureInfo("en-US"); + + private Grammar heyDalamudGrammar; + + private SpeechRecognitionEngine recognizer; + + private DetectionState state; + private readonly SpeechSynthesizer synthesizer = new(); + + public HeyDalamudPlugin() + { + this.clientState = Service.Get(); + this.dataManager = Service.Get(); + this.commandManager = Service.Get(); + + try + { + Thread.CurrentThread.CurrentCulture = this.definedCulture; + this.SetupSpeech(); + + Log.Information("Voice recognition initialized"); + Chat.Print(PluginName, "Activated", + "Hi, welcome to \"Hey, Dalamud!\". I use the most advanced Allagan Voice Recognition Technology (AVRT) to process your commands. Say \"Hey, Dalamud!\" to get started!"); + } + catch (Exception ex) + { + Chat.Print(PluginName, "Error", + "Could not start voice recognition. Please make sure that you have the American English Windows Language Pack installed."); + Log.Error(ex, "Could not init voice recognition"); + } + } + + public void Dispose() + { + this.synthesizer.Dispose(); + this.recognizer.RecognizeAsyncStop(); + this.recognizer.Dispose(); + } + + [DllImport("winmm.dll")] + public static extern int waveInGetNumDevs(); + + private void SetupSpeech() + { + this.state = DetectionState.Hotword; + + this.recognizer?.RecognizeAsyncStop(); + this.recognizer?.Dispose(); + + this.synthesizer.SetOutputToDefaultAudioDevice(); + + this.recognizer = new SpeechRecognitionEngine(this.definedCulture); + + var numDevs = waveInGetNumDevs(); + Log.Information("[REC] NumDevs: {0}", numDevs); + + var heyDalamudBuilder = new GrammarBuilder("hey dalamud"); + heyDalamudBuilder.Culture = this.definedCulture; + + this.heyDalamudGrammar = new Grammar(heyDalamudBuilder); + this.heyDalamudGrammar.Name = "heyDalamudGrammar"; + + var actionBuilder = new GrammarBuilder(); + actionBuilder.Culture = this.definedCulture; + actionBuilder.AppendDictation(); + + this.actionGrammar = new Grammar(actionBuilder) + { + Name = "actionGrammar" + }; + + // Create and load a dictation grammar. + this.recognizer.LoadGrammar(this.heyDalamudGrammar); + + // Add a handler for the speech recognized event. + this.recognizer.SpeechRecognized += this.recognizer_SpeechRecognized; + + // Configure input to the speech recognizer. + this.recognizer.SetInputToDefaultAudioDevice(); + + // Start asynchronous, continuous speech recognition. + this.recognizer.RecognizeAsync(RecognizeMode.Multiple); + } + + private void SwitchToActionMode() + { + Log.Information("SwitchToActionMode"); + Chat.Print(PluginName, "Listening", "Allagan Voice Recognition Technology is now active."); + UIModule.PlaySound(StartSoundId, 0, 0, 0); + + this.recognizer.RecognizeAsyncStop(); + this.state = DetectionState.Action; + + this.recognizer.UnloadAllGrammars(); + this.recognizer.LoadGrammar(this.actionGrammar); + + this.recognizer.SetInputToDefaultAudioDevice(); + this.recognizer.RecognizeAsync(RecognizeMode.Single); + + Task.Run( + () => + { + Thread.Sleep(9000); + + if (this.state != DetectionState.Action) return; + UIModule.PlaySound(ErrorSoundId, 0, 0, 0); + + this.synthesizer.SpeakAsync("Sorry, I didn't quite get that, please try again."); + this.recognizer.RecognizeAsyncStop(); + + this.recognizer.UnloadAllGrammars(); + this.recognizer.LoadGrammar(this.heyDalamudGrammar); + + this.state = DetectionState.Hotword; + this.recognizer.SetInputToDefaultAudioDevice(); + this.recognizer.RecognizeAsync(RecognizeMode.Multiple); + }, this.noComprehendTaskCancellationTokenSource.Token + ); + } + + private async void recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e) + { + Log.Information("[REC] In mode: {0} Recognized text: {1}", this.state, e.Result.Text); + + try + { + switch (this.state) + { + case DetectionState.Hotword: + this.SwitchToActionMode(); + break; + + case DetectionState.Action: + this.state = DetectionState.Hotword; + + try + { + Chat.Print(PluginName, "Recognized", e.Result.Text); + this.noComprehendTaskCancellationTokenSource.Cancel(); + + this.recognizer.RecognizeAsyncStop(); + + QueryRequestPayload payload; + if (this.clientState.LocalPlayer != null) + { + var territoryType = this.dataManager.GetExcelSheet()! + .GetRow(this.clientState.TerritoryType); + + var activeDuty = territoryType?.ContentFinderCondition.Value?.Name.RawString; + var activeArea = territoryType?.PlaceName.Value?.Name.RawString; + + payload = new QueryRequestPayload + { + IsInGame = true, + Query = e.Result.Text, + CharacterFirstName = this.clientState.LocalPlayer?.Name.TextValue.Split(' ')[0], + ActiveAreaName = string.IsNullOrEmpty(activeArea) ? null : activeArea, + ActiveDutyName = string.IsNullOrEmpty(activeDuty) ? null : activeDuty, + }; + } + else + { + payload = new QueryRequestPayload + { + IsInGame = false, + Query = e.Result.Text, + }; + } + + UIModule.PlaySound(StopSoundId, 0, 0, 0); + + // make the request + var json = JsonConvert.SerializeObject(payload); + Log.Debug("[REC] Sending request: {0}", json); + var response = await Util.HttpClient.PostAsync( + $"{ApiEndpoint}/Query", + new StringContent(json, Encoding.UTF8, "application/json") + ); + var responseData = await response.Content.ReadAsStringAsync(); + Log.Debug("[REC] Got response: {0}", responseData); + + var responseObject = JsonConvert.DeserializeObject(responseData); + if (!string.IsNullOrEmpty(responseObject.Response)) + { + Chat.Print(PluginName, "Response", responseObject.Response); + this.synthesizer.SpeakAsync(responseObject.Response); + } + + if (!string.IsNullOrEmpty(responseObject.Command)) + { + Log.Information("[REC] Executing command: {0}", responseObject.Command); + this.commandManager.ProcessCommand(responseObject.Command); + } + } finally + { + this.recognizer.UnloadGrammar(this.actionGrammar); + this.recognizer.LoadGrammar(this.heyDalamudGrammar); + + this.recognizer.SetInputToDefaultAudioDevice(); + this.recognizer.RecognizeAsync(RecognizeMode.Multiple); + } + + break; + } + } + catch (Exception ex) + { + Log.Error(ex, "Error in voice handling"); + Chat.Print(PluginName, "Error", "Could not handle your voice input. Please try again."); + } + } + + private class QueryRequestPayload + { + public string Query { get; set; } + + public bool IsInGame { get; set; } + public string? CharacterFirstName { get; set; } + public string? ActiveDutyName { get; set; } + public string? ActiveAreaName { get; set; } + } + + private class QueryResponsePayload + { + public bool Success { get; set; } + public string Response { get; set; } + public string? Command { get; set; } + } +} From a4215b2e3d646ffc221bc8754c43fca04adcb7bd Mon Sep 17 00:00:00 2001 From: NotNite Date: Thu, 30 Mar 2023 10:56:47 -0400 Subject: [PATCH 26/34] Add more messages to YesSoliciting --- Dalamud/Fools/Plugins/YesSolicitingPlugin.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs b/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs index 8058e7d87..ef1729890 100644 --- a/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs +++ b/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs @@ -74,16 +74,21 @@ public class YesSolicitingPlugin : IFoolsPlugin "Like to ERP? Join our Extraordinary Raid Party today!", "Bored? Hungry? Visit the Alternate Reality Plugins section today!", - "Need to protect your eyes from stingy bugs? Get BeeShade! Now with 39% less malware.", "Selling iTomestone 14 Pro - has 0.5x mechanic zoom camera, /tell if interested", "buying gf 10k gil", + "ULTIMATE TWENTY NINE HOUR RAID SESSION BEGINS NOW. WATCH LIVE AT twitch.tomestone/xXx_HARDCORE_GAMING_xXx", + + // Copilot wrote this joke and it was so funny I had to keep it + "looking for group to clear ultimates with. i am 2000+ ilvl tank tell if interested", + "Are you looking for a night out of fun? Want to meet new people? See things you've never seen before? Meet me at the back alley of Ul'dah at 10pm tonight! Bring a robe.", }; var message = messages[Random.Shared.Next(0, messages.Count)]; this.chatGui.PrintChat(new XivChatEntry { - Message = $"[YesSoliciting] {firstName} {lastName}: {message}", + Name = $"[YesSoliciting] {firstName} {lastName}", + Message = message, Type = XivChatType.Shout, }); } From deaae392135ea2cbfb7853620ce18f098735939c Mon Sep 17 00:00:00 2001 From: Bernardo Lopes Date: Thu, 30 Mar 2023 15:37:16 -0300 Subject: [PATCH 27/34] Update YesSolicitingPlugin.cs --- Dalamud/Fools/Plugins/YesSolicitingPlugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs b/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs index ef1729890..c2a98f0a3 100644 --- a/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs +++ b/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs @@ -74,7 +74,7 @@ public class YesSolicitingPlugin : IFoolsPlugin "Like to ERP? Join our Extraordinary Raid Party today!", "Bored? Hungry? Visit the Alternate Reality Plugins section today!", - "Selling iTomestone 14 Pro - has 0.5x mechanic zoom camera, /tell if interested", + "Selling iTomestone 14 Pro - has world-first 0.5x mechanic zoom camera, /tell if interested", "buying gf 10k gil", "ULTIMATE TWENTY NINE HOUR RAID SESSION BEGINS NOW. WATCH LIVE AT twitch.tomestone/xXx_HARDCORE_GAMING_xXx", From afe1e2169f32e58bbfdb583a5602ea929db8eec1 Mon Sep 17 00:00:00 2001 From: chirp Date: Fri, 31 Mar 2023 11:13:59 +0100 Subject: [PATCH 28/34] Update OopsMaybeLalafellsPlugin.cs --- Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs index 1b0bce47d..de3c64e55 100644 --- a/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs +++ b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs @@ -14,12 +14,13 @@ namespace Dalamud.Fools.Plugins; public class OopsMaybeLalafells : IFoolsPlugin { - // Plugin + // Oops, Maybe Lalafells? + // This plugin is deliberately nerfed to prevent a fully-formed revival of the original. public OopsMaybeLalafells() { var scanner = Service.Get(); - var addr = scanner.Module.BaseAddress + 0x0484F60; + var addr = scanner.Module.BaseAddress + 0x0484F60; // Deliberate choice in line with the above comment - this is intended to break after the next patch. SetupCharacterHook = Hook.FromAddress(addr, SetupCharacterDetour); SetupCharacterHook.Enable(); RedrawAll(); From e5204e4876faa6fa211edfd9397a3a5e710d4e4d Mon Sep 17 00:00:00 2001 From: goat Date: Fri, 31 Mar 2023 20:03:45 +0200 Subject: [PATCH 29/34] set cs to master commit --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 9e97396f9..74345c974 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 9e97396f98c20eff17a71aacb24296efcaa04808 +Subproject commit 74345c97468f310d555929f4e40c844556bc64e7 From fd66645f47d4aacea2b8a1c2f582e46c8b08f794 Mon Sep 17 00:00:00 2001 From: goat Date: Fri, 31 Mar 2023 20:19:15 +0200 Subject: [PATCH 30/34] add AppearCondition.Fools23 for installer category which checks date, remove popup for now --- Dalamud/Fools/FoolsManager.cs | 8 ++++++++ .../Internal/PluginCategoryManager.cs | 4 +++- .../PluginInstaller/PluginInstallerWindow.cs | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 336aed539..f929fdd5d 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -165,6 +165,12 @@ internal class FoolsManager : IDisposable, IServiceType }; } + public bool CheckIsApplicableAprilFoolsTime() + { + var now = DateTime.Now; + return now is { Year: 2023, Month: 4, Day: 1 }; + } + public void ActivatePlugin(string plugin) { if (this.ActivatedPlugins.ContainsKey(plugin)) @@ -238,10 +244,12 @@ internal class FoolsManager : IDisposable, IServiceType return; } + /* var di = Service.Get(); di.OpenFoolsWindow(); dalamudConfig.HasSeenFools23 = true; dalamudConfig.QueueSave(); + */ } } diff --git a/Dalamud/Interface/Internal/PluginCategoryManager.cs b/Dalamud/Interface/Internal/PluginCategoryManager.cs index 1a8acfa3f..9b322a78b 100644 --- a/Dalamud/Interface/Internal/PluginCategoryManager.cs +++ b/Dalamud/Interface/Internal/PluginCategoryManager.cs @@ -28,7 +28,7 @@ internal class PluginCategoryManager new(11, "special.devIconTester", () => Locs.Category_IconTester), new(12, "special.dalamud", () => Locs.Category_Dalamud), new(13, "special.plugins", () => Locs.Category_Plugins), - new(14, "special.alternateReality", () => "Alternate Reality"), + new(14, "special.alternateReality", () => "Alternate Reality", CategoryInfo.AppearCondition.Fools23), new(FirstTagBasedCategoryId + 0, "other", () => Locs.Category_Other), new(FirstTagBasedCategoryId + 1, "jobs", () => Locs.Category_Jobs), new(FirstTagBasedCategoryId + 2, "ui", () => Locs.Category_UI), @@ -362,6 +362,8 @@ internal class PluginCategoryManager /// Check if plugin testing is enabled. /// DoPluginTest, + + Fools23, } /// diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index e934ea71b..e87803179 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -1150,6 +1150,10 @@ internal class PluginInstallerWindow : Window, IDisposable if (!Service.Get().DoPluginTest) continue; break; + case PluginCategoryManager.CategoryInfo.AppearCondition.Fools23: + if (!Service.Get().CheckIsApplicableAprilFoolsTime()) + continue; + break; default: throw new ArgumentOutOfRangeException(); } @@ -1293,6 +1297,20 @@ internal class PluginInstallerWindow : Window, IDisposable { var manager = Service.Get(); + ImGui.TextWrapped(@" +A team of scientists and plugin developers have collaborated to create a new +version of Dalamud that includes plugins from other realities. + +With our high tech systems, the plugin installer now offers new and unique +plugins with endless possibilities. + +We hope you enjoy this new version of Dalamud, and we look forward to your feedback. +".Trim()); + + ImGuiHelpers.ScaledDummy(8); + ImGui.SameLine(); + ImGuiHelpers.ScaledDummy(8); + foreach (var plugin in manager.FoolsPlugins) { // dropdown From 2309a463d0724a129c3e0694b3945845f8220b37 Mon Sep 17 00:00:00 2001 From: goat Date: Fri, 31 Mar 2023 20:21:23 +0200 Subject: [PATCH 31/34] take out YesSoliciting --- Dalamud/Fools/FoolsManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index f929fdd5d..4ecc22b3e 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -117,6 +117,7 @@ internal class FoolsManager : IDisposable, IServiceType RealAuthor = "Chirp", Type = typeof(CatBubblesPlugin), }, + /* new() { Name = "YesSoliciting", @@ -126,6 +127,7 @@ internal class FoolsManager : IDisposable, IServiceType RealAuthor = "NotNite", Type = typeof(YesSolicitingPlugin), }, + */ new() { Name = "GoodVibes", From be1e5be02d9ed0874ef9be8044b5a9a18bf0abc6 Mon Sep 17 00:00:00 2001 From: goat Date: Fri, 31 Mar 2023 20:24:26 +0200 Subject: [PATCH 32/34] clarify hey dalamud --- Dalamud/Fools/FoolsManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Fools/FoolsManager.cs b/Dalamud/Fools/FoolsManager.cs index 4ecc22b3e..6aac3b349 100644 --- a/Dalamud/Fools/FoolsManager.cs +++ b/Dalamud/Fools/FoolsManager.cs @@ -159,7 +159,7 @@ internal class FoolsManager : IDisposable, IServiceType { Name = "Hey Dalamud!", InternalName = "HeyDalamudPlugin", - Description = "Scientists have unearthed advanced Allagan Voice Recognition Technology from before the Calamity, then they used it in a Dalamud plugin. Was it a good idea? That's for you to decide.", + Description = "Scientists have unearthed advanced Allagan Voice Recognition Technology from before the Calamity, then they used it in a Dalamud plugin. Was it a good idea? That's for you to decide.\nVoice recognition is performed locally, it only listens after \"Hey, Dalamud!\" is detected(a sound will play) and none of your prompts will be stored.", Author = "snake", RealAuthor = "Avaflow", Type = typeof(HeyDalamudPlugin), From 7c3b33be25b0a88c1ce3d50c2b4d96f8eb4e8376 Mon Sep 17 00:00:00 2001 From: goat Date: Fri, 31 Mar 2023 20:46:54 +0200 Subject: [PATCH 33/34] comment out command handler in Hey, Dalamud! --- Dalamud/Fools/Plugins/HeyDalamudPlugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs b/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs index ae3efd716..45bada9a4 100644 --- a/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs +++ b/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs @@ -238,7 +238,7 @@ public class HeyDalamudPlugin : IFoolsPlugin if (!string.IsNullOrEmpty(responseObject.Command)) { Log.Information("[REC] Executing command: {0}", responseObject.Command); - this.commandManager.ProcessCommand(responseObject.Command); + //this.commandManager.ProcessCommand(responseObject.Command); } } finally { From 9036f43799963d59854037b1c6aadb2494b6f6f4 Mon Sep 17 00:00:00 2001 From: goat Date: Fri, 31 Mar 2023 21:04:58 +0200 Subject: [PATCH 34/34] sever link for hey, dalamud --- Dalamud/Fools/Plugins/HeyDalamudPlugin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs b/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs index 45bada9a4..acc4913b1 100644 --- a/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs +++ b/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs @@ -22,6 +22,8 @@ using Newtonsoft.Json; namespace Dalamud.Fools.Plugins; +// The server for this one is open-source and available at: https://github.com/avafloww/HeyDalamud + public class HeyDalamudPlugin : IFoolsPlugin { public enum DetectionState