using Dalamud.Game.Command; using Dalamud.Game.Gui.ContextMenu; using Dalamud.IoC; using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; using PlayerNotes.Windows; namespace PlayerNotes; public sealed class Plugin : IDalamudPlugin { [PluginService] internal static IDalamudPluginInterface PluginInterface { get; private set; } = null!; [PluginService] internal static IChatGui ChatGui { get; private set; } = null!; [PluginService] internal static IFramework Framework { get; private set; } = null!; [PluginService] internal static IPlayerState PlayerState { get; private set; } = null!; [PluginService] internal static IPartyList PartyList { get; private set; } = null!; [PluginService] internal static IContextMenu ContextMenu { get; private set; } = null!; [PluginService] internal static ICommandManager CommandManager { get; private set; } = null!; [PluginService] internal static IPluginLog Log { get; private set; } = null!; public const string CommandName = "/playernotes"; internal Storage Storage { get; } private readonly WindowSystem windowSystem = new("PlayerNotes"); private MainWindow mainWindow; private PartyWatcher partyWatcher; public Plugin() { Storage = new Storage(PluginInterface, Log); mainWindow = new MainWindow(this); windowSystem.AddWindow(mainWindow); partyWatcher = new PartyWatcher(PartyList, PlayerState, Storage, ChatGui); CommandManager.AddHandler(CommandName, new CommandInfo(OnCommand) { HelpMessage = "Opens the Player Notes window.", }); PluginInterface.UiBuilder.Draw += windowSystem.Draw; PluginInterface.UiBuilder.OpenConfigUi += ToggleWindow; // Right-click context menus (chat player names -> add to friend list / blacklist). ContextMenu.OnMenuOpened += OnContextMenuOpened; Framework.Update += partyWatcher.OnFrameworkUpdate; Log.Information("Player Notes loaded."); } public void Dispose() { Framework.Update -= partyWatcher.OnFrameworkUpdate; ContextMenu.OnMenuOpened -= OnContextMenuOpened; PluginInterface.UiBuilder.Draw -= windowSystem.Draw; PluginInterface.UiBuilder.OpenConfigUi -= ToggleWindow; CommandManager.RemoveHandler(CommandName); windowSystem.RemoveAllWindows(); } private void OnCommand(string command, string args) => ToggleWindow(); public void ToggleWindow() => mainWindow.Toggle(); /// /// Adds context menu entries when a right-click happens inside the chat log. /// Filtering on AddonName keeps our items out of every other default context menu. /// private void OnContextMenuOpened(IMenuOpenedArgs e) { if (e.AddonName != "ChatLog") return; var target = e.Target as MenuTargetDefault; if (target is null || string.IsNullOrWhiteSpace(target.TargetName)) return; var name = target.TargetName.Trim(); var world = TargetWorld(target); e.AddMenuItem(new MenuItem { Name = $"Add to Friends: {name}", PrefixChar = 'F', Priority = 10, // below the native menu items OnClicked = _ => AddToPool(Pool.Friends, name, TargetContentId(target), world), }); e.AddMenuItem(new MenuItem { Name = $"Add to Blacklist: {name}", PrefixChar = 'B', Priority = 11, OnClicked = _ => AddToPool(Pool.Blacklist, name, TargetContentId(target), world), }); } private static ulong? TargetContentId(MenuTargetDefault target) => target.TargetContentId == 0 ? null : target.TargetContentId; /// The right-clicked player's home world, or "" when it cannot be determined. private static string TargetWorld(MenuTargetDefault target) { var world = target.TargetHomeWorld; return world.IsValid ? world.Value.Name.ExtractText() : ""; // Lumina sheet rows are structs } /// /// Adds a player to one of the pools (deduplicated by name + world). Prints a confirmation in chat. /// public bool AddToPool(Pool pool, string name, ulong? contentId = null, string world = "") { if (string.IsNullOrWhiteSpace(name)) { ChatGui.PrintError("[PlayerNotes] Type a player name first."); return false; } var poolName = pool == Pool.Friends ? "friend list" : "blacklist"; var poolEntries = Storage.Data.GetPool(pool); if (poolEntries.Any(e => string.Equals(e.Name, name, StringComparison.OrdinalIgnoreCase) && string.Equals(e.World, world, StringComparison.OrdinalIgnoreCase))) { ChatGui.Print($"[PlayerNotes] \"{name}\" is already in your {poolName}."); return false; } poolEntries.Add(new PlayerEntry { Name = name, World = world ?? "", ContentId = contentId, }); Storage.Save(); var worldSuffix = string.IsNullOrEmpty(world) ? "" : $" ({world})"; ChatGui.Print($"[PlayerNotes] Added \"{name}\"{worldSuffix} to your {poolName}."); return true; } }