diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
index f6cf88d90..9a6f63ca3 100644
--- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs
+++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs
@@ -376,6 +376,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/Dalamud.csproj b/Dalamud/Dalamud.csproj
index 63b66f3b6..ac8251f15 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
new file mode 100644
index 000000000..6aac3b349
--- /dev/null
+++ b/Dalamud/Fools/FoolsManager.cs
@@ -0,0 +1,257 @@
+using System;
+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;
+
+// . ///*,.. .,*/((#######((((((((((((#(((########(*,****,,....,**///
+// . (//*,,. ./(#%%%%%%%##((((/////(((((###(#####%#(/**,......,,**/(
+// (*((/*,,,/###%%%%%%%%%%%#((///((((##%%%%%%%%####%#(//,. ..,,*/(*
+// #((/*/(((((#%%%%&&%%%%%##(/(((((#%%%%&%%%%%%%##(((((/,...,,*/(#%
+// %,&%#######%%%%%%%%%%%%%%%%###(####%%%%%%&&&%%%%%%#((((((((/**/(#&%
+// ,,#&%%####%%%%%(//##%%%%%%%%%%#%%%%%%%%%%%%%%%%%%%%%%%%%###(####((#%#
+// ,.%&%%%%%%%%%#/. *#%%%&&&&&&&&&%&&&%%%%%%%%%#*...*(##%%####((##%%%%
+// ..%&&%%%%%%%%#(* ,%&&&&&&&&&&&&&&&&&&&%%%(/, . *(#%%#######%%%
+// /#&&&%%%%%%%%%%#, *#&&&&&&&&&&&&&&&&&&&&&&&&&&%(, , ,(####%%%%####
+// %&&&&%%%%&%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%*. /#%%%%%#%%%%%%
+// (&&%%%%%&&&&&&&&&&&&&%%%%%%%%%%%%##%%&&&&&&&&&&&&&&%%%%###%%%%%%%%%%%%%###
+// *(@&%%%%%&&&&&%%%%%#(#############%%%%%%%&&&&&&&&&&&&&&&%%&%&&&%%%%%%%%%%%%%
+// */&&%%%#((//******/********/(((((((/*/(#%%&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%
+// ((&%/,................,,,,,,,,*,,,,,*,,,,*(%%%%&&&&&&&&&&&&&&&&&%%%%%%%%%%%%
+// . , ...............,,*(%%&&&&&&&&&&&&&&&&&&%%%%%%%%%%
+// ., .,,,**/(#%%%&&&&&&&&&&&&&&%%%%%%%%%
+// . ..,,,**/#%%%&&&&&&&&&&&&&&%%%%%%%%
+// , .,*/#%%%%&&&&&&&&&&&&&%%%%%%%%
+// ,,. ./(#%%%&&&&&&&&%&&&%%##%%%%%
+// ((% ,(#%%%%&&&&&&&%%%&%%%%%%%%%
+// #,%%/ .*#%%%%&&&&&&&&%&%%###%%%%%
+// @(%%( .,(%%%%%%%&&&&&&&%%#%%%%%%%%
+// /%%%* ./#%%%%%%%%%&&&&&%##%%%%%%%(
+// #*&%%#. ..,(#%%%%%%%%%%&&&&&%%%%%%%#((,
+// *(%%#/. .,**(#####%%%%%%%%%%%%%%%%%#**..
+// #((#%##/. ,,*(####%%%%%%%%%%%&&%%%#(,
+// **###((*. ,(*///((####%%%%%%%&&&%(/.,
+// .,(###(//*,. ..,*//(((((##%%%%%%%%%%%%#%.
+// . (#((/**,,.. ..,*//*/////(##%%%%%%%%%(#(*
+// */(((/////,.. .....,,,,***///####%%%%%%%#.
+// //*((((*,.. ...,...,,,.,//((((##%%%%%%%,/
+// * //(/******,,,.,,..,,,****/*///((((#(###(/##(,
+//
+// April fools! <3
+
+///
+/// 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");
+
+ private UiBuilder uiBuilder;
+ private ClientState clientState;
+
+ [ServiceManager.ServiceConstructor]
+ private FoolsManager()
+ {
+ this.uiBuilder = new UiBuilder("fools");
+ 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()
+ {
+ 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 = "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),
+ },
+ new()
+ {
+ Name = "Oops, Maybe Lalafells?",
+ InternalName = "OopsMaybeLalafellsPlugin",
+ Description = "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.",
+ Author = "Chirpopo Chirpo",
+ 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),
+ },
+ /*
+ new()
+ {
+ Name = "YesSoliciting",
+ InternalName = "YesSolicitingPlugin",
+ Description = "Summon annoying shout messages from beyond the rift.",
+ Author = "Anna",
+ RealAuthor = "NotNite",
+ Type = typeof(YesSolicitingPlugin),
+ },
+ */
+ new()
+ {
+ Name = "GoodVibes",
+ InternalName = "GoodVibesPlugin",
+ Description = "Shake things up with this vibe plugin!",
+ Author = "C h i r p",
+ RealAuthor = "Chirp",
+ Type = typeof(GoodVibesPlugin),
+ },
+ new()
+ {
+ Name = "YesHealMe",
+ InternalName = "YesHealMePlugin",
+ 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),
+ },
+ new()
+ {
+ Name = "Complicated Tweaks",
+ InternalName = "ComplicatedTweaksPlugin",
+ Description = "As complicated as it gets!",
+ Author = "Caraxi",
+ 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.\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),
+ },
+ };
+ }
+
+ 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))
+ {
+ 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 void Dispose()
+ {
+ foreach (var plugin in this.ActivatedPlugins.Values)
+ {
+ plugin.Dispose();
+ }
+
+ this.ActivatedPlugins.Clear();
+ ((IDisposable)this.uiBuilder).Dispose();
+ this.clientState.Login -= this.ClientStateOnLogin;
+ }
+
+ 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);
+ }
+
+ 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/FoolsPluginMetadata.cs b/Dalamud/Fools/FoolsPluginMetadata.cs
new file mode 100644
index 000000000..67c657400
--- /dev/null
+++ b/Dalamud/Fools/FoolsPluginMetadata.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Dalamud.Fools;
+
+public class FoolsPluginMetadata
+{
+ public string Name { get; init; }
+
+ public string InternalName { get; init; }
+
+ public string Description { get; init; }
+
+ public string Author { get; init; }
+
+ public string RealAuthor { get; init; }
+
+ public Type Type { get; init; }
+}
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/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/Helper/YesHealMe/Colors.cs b/Dalamud/Fools/Helper/YesHealMe/Colors.cs
new file mode 100644
index 000000000..ab2473199
--- /dev/null
+++ b/Dalamud/Fools/Helper/YesHealMe/Colors.cs
@@ -0,0 +1,9 @@
+using System.Numerics;
+
+namespace Dalamud.Fools.Helper.YesHealMe;
+
+public static class Colors
+{
+ 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
new file mode 100644
index 000000000..b9a571d9c
--- /dev/null
+++ b/Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Numerics;
+using ImGuiNET;
+
+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)
+ {
+ 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);
+
+ 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)
+ {
+ if (!fontManager.GameFont.Available)
+ {
+ return;
+ }
+
+ var font = fontManager.GameFont.ImFont;
+
+ var drawList = ImGui.GetBackgroundDrawList();
+
+ 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..7c7244ba9
--- /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 FontManager(UiBuilder uiBuilder)
+ {
+ 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
new file mode 100644
index 000000000..1c11ee84c
--- /dev/null
+++ b/Dalamud/Fools/Helper/YesHealMe/HudHelper.cs
@@ -0,0 +1,35 @@
+using Dalamud.Game.ClientState.Objects;
+using Dalamud.Game.ClientState.Objects.SubKinds;
+using FFXIVClientStructs.FFXIV.Client.UI.Agent;
+
+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 targetPlayer = hudData->ObjectId;
+
+ 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
new file mode 100644
index 000000000..6f82b0d7f
--- /dev/null
+++ b/Dalamud/Fools/Helper/YesHealMe/IconCache.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Dalamud.Data;
+using Dalamud.Logging;
+using Dalamud.Utility;
+using ImGuiScene;
+
+namespace Dalamud.Fools.Helper.YesHealMe;
+
+public class IconCache : IDisposable
+{
+ private const string IconFilePath = "ui/icon/{0:D3}000/{1:D6}_hr1.tex";
+
+ private static IconCache? internalInstance;
+ private readonly Dictionary iconTextures = new();
+ public static IconCache Instance => internalInstance ??= new IconCache();
+
+ public void Dispose()
+ {
+ foreach (var texture in this.iconTextures.Values)
+ {
+ texture?.Dispose();
+ }
+
+ this.iconTextures.Clear();
+ }
+
+ public static void Cleanup()
+ {
+ 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)
+ {
+ this.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 (this.iconTextures.TryGetValue(iconId, out var value))
+ {
+ return value;
+ }
+
+ this.iconTextures.Add(iconId, null);
+ this.LoadIconTexture(iconId);
+
+ return this.iconTextures[iconId];
+ }
+}
diff --git a/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs b/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs
new file mode 100644
index 000000000..29af9e483
--- /dev/null
+++ b/Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Dalamud.Game;
+using Dalamud.Game.Gui;
+using FFXIVClientStructs.FFXIV.Client.UI;
+
+namespace Dalamud.Fools.Helper.YesHealMe;
+
+public unsafe class PartyListAddon : IEnumerable, IDisposable
+{
+ private readonly List addonData = new();
+
+ public PartyListAddon()
+ {
+ 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 -= this.OnFrameworkUpdate;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return this.addonData.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ private void OnFrameworkUpdate(Framework framework)
+ {
+ 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];
+
+ this.addonData.Add(new PartyListAddonData
+ {
+ PlayerCharacter = playerCharacter,
+ UserInterface = userInterface,
+ });
+ }
+ }
+}
diff --git a/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs b/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs
new file mode 100644
index 000000000..0dbdd3913
--- /dev/null
+++ b/Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs
@@ -0,0 +1,10 @@
+using Dalamud.Game.ClientState.Objects.SubKinds;
+using FFXIVClientStructs.FFXIV.Client.UI;
+
+namespace Dalamud.Fools.Helper.YesHealMe;
+
+public readonly struct PartyListAddonData
+{
+ public AddonPartyList.PartyListMemberStruct UserInterface { get; init; }
+ public PlayerCharacter? PlayerCharacter { get; init; }
+}
diff --git a/Dalamud/Fools/IFoolsPlugin.cs b/Dalamud/Fools/IFoolsPlugin.cs
new file mode 100644
index 000000000..5764dd78d
--- /dev/null
+++ b/Dalamud/Fools/IFoolsPlugin.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace Dalamud.Fools;
+
+public interface IFoolsPlugin : IDisposable
+{
+ public void DrawUi() { }
+}
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/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() { }
+}
diff --git a/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs b/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs
new file mode 100644
index 000000000..c3b8364ed
--- /dev/null
+++ b/Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs
@@ -0,0 +1,66 @@
+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} dish(es) to be cleaned"),
+ new Duty("Taxes", _ => "Taxes need to be filed"),
+ 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;
+
+ 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; }
+ }
+}
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);
+ }
+ }
+}
diff --git a/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs b/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs
new file mode 100644
index 000000000..acc4913b1
--- /dev/null
+++ b/Dalamud/Fools/Plugins/HeyDalamudPlugin.cs
@@ -0,0 +1,280 @@
+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;
+
+// The server for this one is open-source and available at: https://github.com/avafloww/HeyDalamud
+
+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; }
+ }
+}
diff --git a/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs
new file mode 100644
index 000000000..de3c64e55
--- /dev/null
+++ b/Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs
@@ -0,0 +1,106 @@
+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;
+
+namespace Dalamud.Fools.Plugins;
+
+public class OopsMaybeLalafells : IFoolsPlugin
+{
+ // 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; // 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();
+ }
+
+ 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 && obj.ObjectIndex < 301) continue;
+
+ 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 delegate char SetupCharacterDelegate(nint a1, nint a2);
+ private Hook SetupCharacterHook = null!;
+
+ private char SetupCharacterDetour(nint a1, nint a2)
+ {
+ try
+ {
+ var custom = Marshal.PtrToStructure(a2);
+
+ // Roll the dice
+ if (custom.Race != 3 && Rng.Next(0, 4) == 0)
+ {
+ 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;
+ }
+}
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/ScreensaverPlugin.cs b/Dalamud/Fools/Plugins/ScreensaverPlugin.cs
new file mode 100644
index 000000000..ea26c7634
--- /dev/null
+++ b/Dalamud/Fools/Plugins/ScreensaverPlugin.cs
@@ -0,0 +1,71 @@
+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);
+
+ ImGui.End();
+ ImGui.PopStyleVar();
+ }
+
+ public void Dispose()
+ {
+ this.logoTexture.Dispose();
+ }
+}
diff --git a/Dalamud/Fools/Plugins/YesHealMePlugin.cs b/Dalamud/Fools/Plugins/YesHealMePlugin.cs
new file mode 100644
index 000000000..3e2b2c213
--- /dev/null
+++ b/Dalamud/Fools/Plugins/YesHealMePlugin.cs
@@ -0,0 +1,31 @@
+using Dalamud.Fools.Helper.YesHealMe;
+using Dalamud.Interface;
+
+namespace Dalamud.Fools.Plugins;
+
+public class YesHealMePlugin : IFoolsPlugin
+{
+ private readonly FontManager fontManager;
+ private readonly PartyListAddon partyListAddon = new();
+ private int iconId = 1;
+
+ public YesHealMePlugin()
+ {
+ const string nameSpace = "fools+YesHealMe";
+ var uiBuilder = new UiBuilder(nameSpace);
+ this.fontManager = new FontManager(uiBuilder);
+ }
+
+ ///
+ public void Dispose()
+ {
+ this.fontManager.Dispose();
+ this.partyListAddon.Dispose();
+ IconCache.Cleanup();
+ }
+
+ public void DrawUi()
+ {
+ 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..4bb30ab0d
--- /dev/null
+++ b/Dalamud/Fools/Plugins/YesHealMePluginWindow.cs
@@ -0,0 +1,92 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using Dalamud.Fools.Helper.YesHealMe;
+using Dalamud.Game.ClientState;
+using Dalamud.Game.ClientState.Objects.SubKinds;
+using Dalamud.Interface.Internal;
+using ImGuiNET;
+
+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);
+ }
+}
diff --git a/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs b/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs
new file mode 100644
index 000000000..c2a98f0a3
--- /dev/null
+++ b/Dalamud/Fools/Plugins/YesSolicitingPlugin.cs
@@ -0,0 +1,105 @@
+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!",
+ "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",
+
+ // 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
+ {
+ Name = $"[YesSoliciting] {firstName} {lastName}",
+ Message = 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);
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs
index 8848cd481..c184475e8 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;
@@ -63,6 +64,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;
@@ -111,6 +113,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);
@@ -128,6 +131,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;
@@ -339,6 +343,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/PluginCategoryManager.cs b/Dalamud/Interface/Internal/PluginCategoryManager.cs
index 06e306c50..9b322a78b 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", CategoryInfo.AppearCondition.Fools23),
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;
@@ -352,6 +362,8 @@ internal class PluginCategoryManager
/// Check if plugin testing is enabled.
///
DoPluginTest,
+
+ Fools23,
}
///
@@ -403,7 +415,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 +437,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 a2126089b..e87803179 100644
--- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs
@@ -8,9 +8,9 @@ using System.Linq;
using System.Numerics;
using System.Threading;
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;
@@ -114,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;
@@ -263,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.
///
@@ -419,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);
@@ -432,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);
@@ -536,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);
}
@@ -575,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);
@@ -588,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();
@@ -597,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);
}
}
});
@@ -609,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();
@@ -645,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)))
{
@@ -686,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);
@@ -719,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));
@@ -889,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;
@@ -909,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())
{
@@ -1040,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();
@@ -1089,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)
{
@@ -1097,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)
{
@@ -1111,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();
}
@@ -1121,7 +1164,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;
}
@@ -1236,6 +1280,11 @@ internal class PluginInstallerWindow : Window, IDisposable
}
break;
+
+ case PluginCategoryManager.GroupKind.AlternateReality:
+ this.DrawAlternateRealityPlugins();
+ break;
+
default:
this.DrawAvailablePluginList();
break;
@@ -1244,6 +1293,61 @@ internal class PluginInstallerWindow : Window, IDisposable
ImGui.PopStyleVar();
}
+ private void DrawAlternateRealityPlugins()
+ {
+ 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
+ if (ImGui.CollapsingHeader($"{plugin.Name}##AprilFools_Header_{plugin.Name}"))
+ {
+ ImGui.Indent();
+ ImGui.Text(plugin.Name);
+ 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);
+
+ if (manager.IsPluginActivated(plugin.InternalName))
+ {
+ if (ImGui.Button($"Disable##AprilFools_Disable_{plugin.Name}"))
+ {
+ manager.DeactivatePlugin(plugin.InternalName);
+ }
+ }
+ else
+ {
+ if (ImGui.Button($"Install##AprilFools_Enable_{plugin.Name}"))
+ {
+ manager.ActivatePlugin(plugin.InternalName);
+ }
+ }
+
+ ImGui.Unindent();
+ }
+ }
+ }
+
private void DrawImageTester()
{
var sectionSize = ImGuiHelpers.GlobalScale * 66;
@@ -1255,7 +1359,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();
@@ -1319,7 +1424,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";
@@ -1349,7 +1455,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 |
@@ -1424,9 +1531,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);
@@ -1434,19 +1543,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"))
@@ -1518,7 +1632,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();
@@ -1533,7 +1650,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)
{
@@ -1720,7 +1838,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;
@@ -1807,7 +1926,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);
@@ -1834,7 +1954,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}"
@@ -1856,7 +1977,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
@@ -1865,11 +1987,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));
}
}
@@ -2012,7 +2138,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)
@@ -2064,7 +2191,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);
@@ -2080,7 +2209,8 @@ 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();
@@ -2116,7 +2246,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())
@@ -2162,9 +2293,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);
@@ -2190,7 +2324,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);
@@ -2212,7 +2348,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();
@@ -2264,7 +2401,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;
@@ -2274,7 +2412,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;
@@ -2313,7 +2452,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)
@@ -2323,7 +2463,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;
@@ -2331,7 +2472,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
@@ -2549,7 +2691,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)
@@ -2654,7 +2797,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++)
{
@@ -2728,16 +2875,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);
@@ -2784,7 +2937,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));
@@ -2797,9 +2951,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) =>
@@ -2811,7 +2970,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.");
@@ -2897,8 +3057,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
@@ -2911,7 +3073,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");
@@ -2941,21 +3104,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
@@ -2973,11 +3142,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)");
@@ -2989,11 +3161,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!");
@@ -3001,56 +3176,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");
@@ -3060,61 +3258,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
@@ -3122,13 +3347,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");
@@ -3142,7 +3369,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");
@@ -3158,29 +3388,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.");
@@ -3190,43 +3454,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
@@ -3244,7 +3536,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
}