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;
}
}