using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Dalamud.Game; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; using XivCommon.Functions.NamePlates; namespace NominaOcculta { internal class Obscurer : IDisposable { private Plugin Plugin { get; } private Stopwatch UpdateTimer { get; } = new(); private IList Friends { get; set; } internal Obscurer(Plugin plugin) { this.Plugin = plugin; this.UpdateTimer.Start(); this.Friends = this.Plugin.Common.Functions.FriendList.List .Select(friend => friend.Name.TextValue) .ToList(); this.Plugin.Framework.Update += this.OnFrameworkUpdate; this.Plugin.Functions.AtkTextNodeSetText += this.OnAtkTextNodeSetText; this.Plugin.Common.Functions.NamePlates.OnUpdate += this.OnNamePlateUpdate; this.Plugin.ChatGui.ChatMessage += this.OnChatMessage; } public void Dispose() { this.Plugin.ChatGui.ChatMessage -= this.OnChatMessage; this.Plugin.Common.Functions.NamePlates.OnUpdate -= this.OnNamePlateUpdate; this.Plugin.Functions.AtkTextNodeSetText -= this.OnAtkTextNodeSetText; this.Plugin.Framework.Update -= this.OnFrameworkUpdate; } private void OnFrameworkUpdate(Framework framework) { if (this.UpdateTimer.Elapsed < TimeSpan.FromSeconds(5)) { return; } this.Friends = this.Plugin.Common.Functions.FriendList.List .Select(friend => friend.Name.TextValue) .ToList(); this.UpdateTimer.Restart(); } private void OnAtkTextNodeSetText(IntPtr node, IntPtr textPtr, ref SeString? overwrite) { // A catch-all for UI text. This is slow, so specialised methods should be preferred. var text = Util.ReadRawSeString(textPtr); var changed = this.ChangeNames(text); if (changed) { overwrite = text; } } private void OnNamePlateUpdate(NamePlateUpdateEventArgs args) { this.ChangeNames(args.Name); this.ChangeNames(args.Title); // for minions } private void OnChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) { this.ChangeNames(sender); this.ChangeNames(message); } private bool ChangeNames(SeString text) { if (!this.Plugin.Config.Enabled || !this.Plugin.NameRepository.Initialised) { return false; } var changed = false; var player = this.Plugin.ClientState.LocalPlayer; if (this.Plugin.Config.SelfFull) { var playerName = player?.Name.TextValue; if (playerName != null && text.ContainsPlayerName(playerName) && this.Plugin.NameRepository.GetReplacement(playerName, GetInfo(player!)) is { } replacement) { text.ReplacePlayerName(playerName, replacement); changed = true; } } if (this.Plugin.Config.SelfFirst || this.Plugin.Config.SelfLast) { var playerName = player?.Name.TextValue; if (playerName != null && this.Plugin.NameRepository.GetReplacement(playerName, GetInfo(player!)) is { } replacement) { var parts = playerName.Split(' ', 2); var replacementParts = replacement.Split(' ', 2); if (this.Plugin.Config.SelfFirst && text.ContainsPlayerName(parts[0])) { text.ReplacePlayerName(parts[0], replacementParts[0]); changed = true; } if (this.Plugin.Config.SelfLast && text.ContainsPlayerName(parts[1])) { text.ReplacePlayerName(parts[1], replacementParts[1]); changed = true; } } } if (this.Plugin.Config.Party) { foreach (var member in this.Plugin.PartyList) { var name = member.Name.TextValue; var info = ((byte) 0xFF, (byte) 0xFF, member.Sex); if (member.GameObject is Character chara) { info = GetInfo(chara); } if (member.ObjectId == player?.ObjectId || !text.ContainsPlayerName(name) || this.Plugin.NameRepository.GetReplacement(name, info) is not { } replacement) { continue; } if (this.Plugin.Config.ExcludeFriends && this.Friends.Contains(name)) { continue; } text.ReplacePlayerName(name, replacement); changed = true; } } if (this.Plugin.Config.Others) { var party = this.Plugin.PartyList.Select(member => member.ObjectId).ToList(); foreach (var obj in this.Plugin.ObjectTable) { if (obj.ObjectKind != ObjectKind.Player || obj is not Character chara || obj.ObjectId == player?.ObjectId || party.Contains(obj.ObjectId)) { continue; } var name = chara.Name.TextValue; if (this.Plugin.Config.ExcludeFriends && this.Friends.Contains(name)) { continue; } var info = GetInfo(chara); if (info.race == 0) { continue; } if (this.Plugin.NameRepository.GetReplacement(name, GetInfo(chara)) is not { } replacement) { continue; } text.ReplacePlayerName(name, replacement); changed = true; } } return changed; } private static (byte race, byte clan, byte gender) GetInfo(Character chara) { return ( chara.Customize[(byte) CustomizeIndex.Race], (byte) ((chara.Customize[(byte) CustomizeIndex.Tribe] - 1) % 2), chara.Customize[(byte) CustomizeIndex.Gender] ); } } }