mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-27 19:09:18 +01:00
Merge pull request #1169 from goaaats/fools23
This commit is contained in:
commit
5e4e530cfe
28 changed files with 2227 additions and 167 deletions
|
|
@ -376,6 +376,8 @@ internal sealed class DalamudConfiguration : IServiceType
|
|||
/// </summary>
|
||||
public double UiBuilderHitch { get; set; } = 100;
|
||||
|
||||
public bool HasSeenFools23 { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Load a configuration from the provided path.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@
|
|||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="7.0.0" />
|
||||
<PackageReference Include="System.Resources.Extensions" Version="7.0.0" />
|
||||
<PackageReference Include="System.Speech" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dalamud.Interface\Dalamud.Interface.csproj" />
|
||||
|
|
|
|||
257
Dalamud/Fools/FoolsManager.cs
Normal file
257
Dalamud/Fools/FoolsManager.cs
Normal file
|
|
@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Manager for all the IFoolsPlugin instances.
|
||||
/// </summary>
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal class FoolsManager : IDisposable, IServiceType
|
||||
{
|
||||
public readonly List<FoolsPluginMetadata> FoolsPlugins = new();
|
||||
public readonly Dictionary<string, IFoolsPlugin> 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<ClientState>.Get();
|
||||
this.clientState.Login += this.ClientStateOnLogin;
|
||||
|
||||
// reflect over all IFoolsPlugin implementations sometime(?)
|
||||
this.FoolsPlugins = new List<FoolsPluginMetadata>
|
||||
{
|
||||
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<DalamudConfiguration>.Get();
|
||||
|
||||
#if !DEBUG
|
||||
if (DateTime.Now is not { Month: 4, Day: 1 })
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dalamudConfig.HasSeenFools23)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
var di = Service<DalamudInterface>.Get();
|
||||
di.OpenFoolsWindow();
|
||||
|
||||
dalamudConfig.HasSeenFools23 = true;
|
||||
dalamudConfig.QueueSave();
|
||||
*/
|
||||
}
|
||||
}
|
||||
18
Dalamud/Fools/FoolsPluginMetadata.cs
Normal file
18
Dalamud/Fools/FoolsPluginMetadata.cs
Normal file
|
|
@ -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; }
|
||||
}
|
||||
61
Dalamud/Fools/FoolsWindow.cs
Normal file
61
Dalamud/Fools/FoolsWindow.cs
Normal file
|
|
@ -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<InterfaceManager>.Get();
|
||||
var dalamud = Service<Dalamud>.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<DalamudInterface>.Get();
|
||||
di.OpenPluginInstallerFools();
|
||||
}
|
||||
|
||||
imgCursor.X += 500;
|
||||
ImGui.SetCursorPos(imgCursor);
|
||||
|
||||
ImGui.Image(this.logoTexture.ImGuiHandle, new Vector2(100));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.logoTexture.Dispose();
|
||||
}
|
||||
}
|
||||
32
Dalamud/Fools/Helper/Chat.cs
Normal file
32
Dalamud/Fools/Helper/Chat.cs
Normal file
|
|
@ -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<ChatGui>.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Dalamud/Fools/Helper/YesHealMe/Colors.cs
Normal file
9
Dalamud/Fools/Helper/YesHealMe/Colors.cs
Normal file
|
|
@ -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);
|
||||
}
|
||||
101
Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs
Normal file
101
Dalamud/Fools/Helper/YesHealMe/DrawUtilities.cs
Normal file
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
20
Dalamud/Fools/Helper/YesHealMe/FontManager.cs
Normal file
20
Dalamud/Fools/Helper/YesHealMe/FontManager.cs
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
35
Dalamud/Fools/Helper/YesHealMe/HudHelper.cs
Normal file
35
Dalamud/Fools/Helper/YesHealMe/HudHelper.cs
Normal file
|
|
@ -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<ObjectTable>.Get().SearchById(objectId);
|
||||
|
||||
if (result?.GetType() == typeof(PlayerCharacter))
|
||||
{
|
||||
return result as PlayerCharacter;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
71
Dalamud/Fools/Helper/YesHealMe/IconCache.cs
Normal file
71
Dalamud/Fools/Helper/YesHealMe/IconCache.cs
Normal file
|
|
@ -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<uint, TextureWrap?> 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<DataManager>.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];
|
||||
}
|
||||
}
|
||||
58
Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs
Normal file
58
Dalamud/Fools/Helper/YesHealMe/PartyListAddon.cs
Normal file
|
|
@ -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<PartyListAddonData>, IDisposable
|
||||
{
|
||||
private readonly List<PartyListAddonData> addonData = new();
|
||||
|
||||
public PartyListAddon()
|
||||
{
|
||||
Service<Framework>.Get().Update += this.OnFrameworkUpdate;
|
||||
}
|
||||
|
||||
private static AddonPartyList* PartyList => (AddonPartyList*)Service<GameGui>.Get()?.GetAddonByName("_PartyList");
|
||||
private static bool DataAvailable => PartyList != null && PartyList->AtkUnitBase.RootNode != null;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Service<Framework>.Get().Update -= this.OnFrameworkUpdate;
|
||||
}
|
||||
|
||||
public IEnumerator<PartyListAddonData> 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs
Normal file
10
Dalamud/Fools/Helper/YesHealMe/PartyListAddonData.cs
Normal file
|
|
@ -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; }
|
||||
}
|
||||
8
Dalamud/Fools/IFoolsPlugin.cs
Normal file
8
Dalamud/Fools/IFoolsPlugin.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Fools;
|
||||
|
||||
public interface IFoolsPlugin : IDisposable
|
||||
{
|
||||
public void DrawUi() { }
|
||||
}
|
||||
135
Dalamud/Fools/Plugins/CatBubblesPlugin.cs
Normal file
135
Dalamud/Fools/Plugins/CatBubblesPlugin.cs
Normal file
|
|
@ -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<ClientState>.Get();
|
||||
|
||||
var sigscanner = Service<SigScanner>.Get();
|
||||
|
||||
var openAddr = sigscanner.ScanText("E8 ?? ?? ?? ?? C7 43 ?? ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ??");
|
||||
BalloonOpen = Marshal.GetDelegateForFunctionPointer<BalloonOpenDelegate>(openAddr);
|
||||
|
||||
var updateAddr = sigscanner.ScanText("48 85 D2 0F 84 ?? ?? ?? ?? 48 89 5C 24 ?? 57 48 83 EC 20 8B 41 0C");
|
||||
BalloonUpdateHook = Hook<BalloonUpdateDelegate>.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<string> strs1 = new() { "mrrp", "nya", "mew", "meow", "mraow", "purr" };
|
||||
private readonly List<string> strs2 = new() { ":3", ":3c", "=^-^=" };
|
||||
private readonly List<string> strs3 = new() { "zxcvbnm,./`-=", "qweasdzxc", "fghjkl;mnbvcxz", "plokmijnuhkjgs" };
|
||||
|
||||
private string GetRandStr(List<string> list)
|
||||
{
|
||||
var x = Rng.Next(list.Count);
|
||||
return list[x];
|
||||
}
|
||||
|
||||
private string GenerateCatSpeak()
|
||||
{
|
||||
var items = new List<string>();
|
||||
|
||||
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<BalloonUpdateDelegate> 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);
|
||||
}
|
||||
}
|
||||
68
Dalamud/Fools/Plugins/ComplicatedTweaksPlugin.cs
Normal file
68
Dalamud/Fools/Plugins/ComplicatedTweaksPlugin.cs
Normal file
|
|
@ -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<Widget> widgets;
|
||||
|
||||
public ComplicatedTweaksPlugin()
|
||||
{
|
||||
this.widgets = new List<Widget>();
|
||||
|
||||
var random = new Random();
|
||||
var possibleWidgets = Enum.GetValues(typeof(Widget)).Cast<Widget>().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() { }
|
||||
}
|
||||
66
Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs
Normal file
66
Dalamud/Fools/Plugins/DailyLifeDutyPlugin.cs
Normal file
|
|
@ -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<Duty> 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<Framework>.Get().Update += this.OnUpdate;
|
||||
this.EmitDutyReminder();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Service<Framework>.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<int, string> message)
|
||||
{
|
||||
this.Tag = tag;
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
internal string Tag { get; init; }
|
||||
|
||||
internal Func<int, string> Message { get; init; }
|
||||
}
|
||||
}
|
||||
46
Dalamud/Fools/Plugins/GoodVibesPlugin.cs
Normal file
46
Dalamud/Fools/Plugins/GoodVibesPlugin.cs
Normal file
|
|
@ -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<SigScanner>.Get().ScanText("48 83 EC 08 8B 02");
|
||||
OffsetModelHook = Hook<OffsetModelDelegate>.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<OffsetModelDelegate> OffsetModelHook = null!;
|
||||
private unsafe nint OffsetModelDetour(nint a1, nint a2)
|
||||
{
|
||||
*(Vector3*)a2 += GenRandVec();
|
||||
return OffsetModelHook.Original(a1, a2);
|
||||
}
|
||||
}
|
||||
}
|
||||
280
Dalamud/Fools/Plugins/HeyDalamudPlugin.cs
Normal file
280
Dalamud/Fools/Plugins/HeyDalamudPlugin.cs
Normal file
|
|
@ -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<ClientState>.Get();
|
||||
this.dataManager = Service<DataManager>.Get();
|
||||
this.commandManager = Service<CommandManager>.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<TerritoryType>()!
|
||||
.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<QueryResponsePayload>(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; }
|
||||
}
|
||||
}
|
||||
106
Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs
Normal file
106
Dalamud/Fools/Plugins/OopsMaybeLalafellsPlugin.cs
Normal file
|
|
@ -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<SigScanner>.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<SetupCharacterDelegate>.FromAddress(addr, SetupCharacterDetour);
|
||||
SetupCharacterHook.Enable();
|
||||
RedrawAll();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SetupCharacterHook.Disable();
|
||||
SetupCharacterHook.Dispose();
|
||||
|
||||
RedrawAll();
|
||||
}
|
||||
|
||||
private unsafe void RedrawAll()
|
||||
{
|
||||
Service<Framework>.Get().RunOnFrameworkThread(() => {
|
||||
var objects = Service<ObjectTable>.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<ushort> 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<SetupCharacterDelegate> SetupCharacterHook = null!;
|
||||
|
||||
private char SetupCharacterDetour(nint a1, nint a2)
|
||||
{
|
||||
try
|
||||
{
|
||||
var custom = Marshal.PtrToStructure<CustomizeData>(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;
|
||||
}
|
||||
}
|
||||
53
Dalamud/Fools/Plugins/PixelImperfectPlugin.cs
Normal file
53
Dalamud/Fools/Plugins/PixelImperfectPlugin.cs
Normal file
|
|
@ -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<ClientState>.Get();
|
||||
this.gameGui = Service<GameGui>.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() { }
|
||||
}
|
||||
71
Dalamud/Fools/Plugins/ScreensaverPlugin.cs
Normal file
71
Dalamud/Fools/Plugins/ScreensaverPlugin.cs
Normal file
|
|
@ -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<InterfaceManager>.Get();
|
||||
var dalamud = Service<Dalamud>.Get();
|
||||
this.condition = Service<Condition>.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();
|
||||
}
|
||||
}
|
||||
31
Dalamud/Fools/Plugins/YesHealMePlugin.cs
Normal file
31
Dalamud/Fools/Plugins/YesHealMePlugin.cs
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.fontManager.Dispose();
|
||||
this.partyListAddon.Dispose();
|
||||
IconCache.Cleanup();
|
||||
}
|
||||
|
||||
public void DrawUi()
|
||||
{
|
||||
YesHealMePluginWindow.Draw(this.partyListAddon, this.fontManager, ref this.iconId);
|
||||
}
|
||||
}
|
||||
92
Dalamud/Fools/Plugins/YesHealMePluginWindow.cs
Normal file
92
Dalamud/Fools/Plugins/YesHealMePluginWindow.cs
Normal file
|
|
@ -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<PlayerCharacter> Characters(PartyListAddon partyListAddon)
|
||||
{
|
||||
return partyListAddon.Any() ? partyListAddon.Select(pla => pla.PlayerCharacter) : new[] { Service<ClientState>.Get().LocalPlayer };
|
||||
}
|
||||
|
||||
private static List<PlayerCharacter> HurtingCharacters(IEnumerable<PlayerCharacter> characters)
|
||||
{
|
||||
return characters
|
||||
.Where(pc => pc.CurrentHp < pc.MaxHp ||
|
||||
Service<DalamudInterface>.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);
|
||||
}
|
||||
}
|
||||
105
Dalamud/Fools/Plugins/YesSolicitingPlugin.cs
Normal file
105
Dalamud/Fools/Plugins/YesSolicitingPlugin.cs
Normal file
|
|
@ -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<string> firstNames;
|
||||
private readonly List<string> lastNames;
|
||||
|
||||
private long nextUpdate;
|
||||
|
||||
public YesSolicitingPlugin()
|
||||
{
|
||||
this.framework = Service<Framework>.Get();
|
||||
this.framework.Update += this.OnUpdate;
|
||||
|
||||
this.chatGui = Service<ChatGui>.Get();
|
||||
|
||||
var dataManager = Service<DataManager>.Get();
|
||||
var charaMakeName = dataManager.GetExcelSheet<CharaMakeName>()!;
|
||||
|
||||
this.firstNames = new List<string>();
|
||||
this.lastNames = new List<string>();
|
||||
|
||||
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<string>
|
||||
{
|
||||
// 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 ~-!<L>@#",
|
||||
"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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
/// </summary>
|
||||
Changelog,
|
||||
|
||||
/// <summary>
|
||||
/// April fools!
|
||||
/// </summary>
|
||||
AlternateReality
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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
|
|||
/// <param name="tag">Tag to match.</param>
|
||||
/// <param name="nameFunc">Function returning localized name of category.</param>
|
||||
/// <param name="condition">Condition to be checked when deciding whether this category should be shown.</param>
|
||||
public CategoryInfo(int categoryId, string tag, Func<string> nameFunc, AppearCondition condition = AppearCondition.None)
|
||||
public CategoryInfo(
|
||||
int categoryId, string tag, Func<string> nameFunc, AppearCondition condition = AppearCondition.None)
|
||||
{
|
||||
this.CategoryId = categoryId;
|
||||
this.Tag = tag;
|
||||
|
|
@ -352,6 +362,8 @@ internal class PluginCategoryManager
|
|||
/// Check if plugin testing is enabled.
|
||||
/// </summary>
|
||||
DoPluginTest,
|
||||
|
||||
Fools23,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue