diff --git a/PlayerTags/Features/ChatTagTargetFeature.cs b/PlayerTags/Features/ChatTagTargetFeature.cs index 22d1560..9e25e90 100644 --- a/PlayerTags/Features/ChatTagTargetFeature.cs +++ b/PlayerTags/Features/ChatTagTargetFeature.cs @@ -5,6 +5,7 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using FFXIVClientStructs.FFXIV.Client.UI.Misc; using Lumina.Excel.GeneratedSheets; +using Pilz.Dalamud.Tools.Strings; using PlayerTags.Configuration; using PlayerTags.Configuration.GameConfig; using PlayerTags.Data; @@ -337,7 +338,7 @@ namespace PlayerTags.Features var stringMatches = GetStringMatches(message); foreach (var stringMatch in stringMatches) { - Dictionary stringChanges = new Dictionary(); + StringChanges stringChanges = new(); if (stringMatch.GameObject is PlayerCharacter playerCharacter) { @@ -364,7 +365,7 @@ namespace PlayerTags.Features var generatedName = BuildPlayername(RandomNameGenerator.Generate(playerName)); if (generatedName != null) { - AddPayloadChanges(TagPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), stringChanges, false); + AddPayloadChanges(StringPosition.Replace, Enumerable.Empty().Append(new TextPayload(generatedName)), stringChanges, false); } } } @@ -396,7 +397,7 @@ namespace PlayerTags.Features { var insertBehindNumberPrefix = tag.InsertBehindNumberPrefixInChat?.Value ?? true; var insertPositionInChat = tag.TagPositionInChat.InheritedValue.Value; - AddPayloadChanges(insertPositionInChat, payloads, stringChanges, insertBehindNumberPrefix); + AddPayloadChanges((StringPosition)insertPositionInChat, payloads, stringChanges, insertBehindNumberPrefix); } // An additional step to apply text color to additional locations diff --git a/PlayerTags/Features/NameplateTagTargetFeature.cs b/PlayerTags/Features/NameplateTagTargetFeature.cs index 9e30923..869e968 100644 --- a/PlayerTags/Features/NameplateTagTargetFeature.cs +++ b/PlayerTags/Features/NameplateTagTargetFeature.cs @@ -3,6 +3,8 @@ using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; using Lumina.Excel.GeneratedSheets; +using Pilz.Dalamud.Nameplates.Tools; +using Pilz.Dalamud.Tools.Strings; using PlayerTags.Configuration; using PlayerTags.Data; using PlayerTags.GameInterface.Nameplates; @@ -129,19 +131,13 @@ namespace PlayerTags.Features /// The position of the changes. /// The payload changes to add. /// The dictionary to add changes to. - private void AddPayloadChanges(NameplateElement nameplateElement, TagPosition tagPosition, IEnumerable payloadChanges, Dictionary> nameplateChanges, bool forceUsingSingleAnchorPayload) + private void AddPayloadChanges(NameplateElement nameplateElement, TagPosition tagPosition, IEnumerable payloadChanges, NameplateChanges nameplateChanges, bool forceUsingSingleAnchorPayload) { - if (!payloadChanges.Any()) + if (payloadChanges.Any()) { - return; + var changes = nameplateChanges.GetChanges((NameplateElements)nameplateElement); + AddPayloadChanges((StringPosition)tagPosition, payloadChanges, changes, forceUsingSingleAnchorPayload); } - - if (!nameplateChanges.Keys.Contains(nameplateElement)) - { - nameplateChanges[nameplateElement] = new(); - } - - AddPayloadChanges(tagPosition, payloadChanges, nameplateChanges[nameplateElement], forceUsingSingleAnchorPayload); } /// @@ -153,7 +149,10 @@ namespace PlayerTags.Features /// The free company text to change. private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany) { - Dictionary> nameplateChanges = new(); + NameplateChanges nameplateChanges = new(); + nameplateChanges.GetProps(NameplateElements.Name).Destination = name; + nameplateChanges.GetProps(NameplateElements.Title).Destination = title; + nameplateChanges.GetProps(NameplateElements.FreeCompany).Destination = freeCompany; if (gameObject is PlayerCharacter playerCharacter) { @@ -197,20 +196,7 @@ namespace PlayerTags.Features } // Build the final strings out of the payloads - foreach ((var nameplateElement, var stringChanges) in nameplateChanges) - { - SeString? seString = null; - - if (nameplateElement == NameplateElement.Name) - seString = name; - else if (nameplateElement == NameplateElement.Title) - seString = title; - else if (nameplateElement == NameplateElement.FreeCompany) - seString = freeCompany; - - if (seString != null) - ApplyStringChanges(seString, stringChanges); - } + ApplyNameplateChanges(nameplateChanges); if (gameObject is PlayerCharacter playerCharacter1) { @@ -234,5 +220,14 @@ namespace PlayerTags.Features } } } + + protected void ApplyNameplateChanges(NameplateChanges nameplateChanges) + { + var props = new NameplateChangesProps + { + Changes = nameplateChanges + }; + NameplateUpdateFactory.ApplyNameplateChanges(props); + } } } diff --git a/PlayerTags/Features/TagTargetFeature.cs b/PlayerTags/Features/TagTargetFeature.cs index ae5d457..7773da5 100644 --- a/PlayerTags/Features/TagTargetFeature.cs +++ b/PlayerTags/Features/TagTargetFeature.cs @@ -5,6 +5,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads; using FFXIVClientStructs.FFXIV.Client.Game.Object; using Lumina.Excel.GeneratedSheets; using Pilz.Dalamud.ActivityContexts; +using Pilz.Dalamud.Tools.Strings; using PlayerTags.Configuration.GameConfig; using PlayerTags.Data; using PlayerTags.Inheritables; @@ -21,12 +22,6 @@ namespace PlayerTags.Features /// public abstract class TagTargetFeature : IDisposable { - protected class StringChanges - { - public List Payloads { get; init; } = new(); - public bool ForceUsingSingleAnchorPayload { get; set; } = false; - } - public ActivityContextManager ActivityContextManager { get; init; } public TagTargetFeature() @@ -154,68 +149,6 @@ namespace PlayerTags.Features return newPayloads.ToArray(); } - /// - /// Adds an additional space text payload in between any existing text payloads. If there is an icon payload between two text payloads then the space is skipped. - /// Also adds an extra space to the beginning or end depending on the tag position and whether the most significant payload in either direction is a text payload. - /// In spirit, this is to ensure there is always a space between 2 text payloads, including between these payloads and the target payload. - /// - /// The payloads to add spaces between. - private void AddSpacesBetweenTextPayloads(List payloads, TagPosition tagPosition) - { - if (payloads == null) - { - return; - } - - if (!payloads.Any()) - { - return; - } - - List indicesToInsertSpacesAt = new List(); - int lastTextPayloadIndex = -1; - foreach (var payload in payloads.Reverse()) - { - if (payload is IconPayload iconPayload) - { - lastTextPayloadIndex = -1; - } - else if (payload is TextPayload textPayload) - { - if (lastTextPayloadIndex != -1) - { - indicesToInsertSpacesAt.Add(payloads.IndexOf(textPayload) + 1); - } - - lastTextPayloadIndex = payloads.IndexOf(textPayload); - } - } - - foreach (var indexToInsertSpaceAt in indicesToInsertSpacesAt) - { - payloads.Insert(indexToInsertSpaceAt, new TextPayload($" ")); - } - - // Decide whether to add a space to the end - if (tagPosition == TagPosition.Before) - { - var significantPayloads = payloads.Where(payload => payload is TextPayload || payload is IconPayload); - if (significantPayloads.Last() is TextPayload) - { - payloads.Add(new TextPayload($" ")); - } - } - // Decide whether to add a space to the beginning - else if (tagPosition == TagPosition.After) - { - var significantPayloads = payloads.Where(payload => payload is TextPayload || payload is IconPayload); - if (significantPayloads.First() is TextPayload) - { - payloads.Insert(0, new TextPayload($" ")); - } - } - } - protected static string BuildPlayername(string name) { var logNameType = GameConfigHelper.Instance.GetLogNameType(); @@ -260,14 +193,11 @@ namespace PlayerTags.Features /// The position to add changes to. /// The payloads to add. /// The dictionary to add the changes to. - protected void AddPayloadChanges(TagPosition tagPosition, IEnumerable payloads, Dictionary stringChanges, bool forceUsingSingleAnchorPayload) + protected void AddPayloadChanges(StringPosition tagPosition, IEnumerable payloads, StringChanges stringChanges, bool forceUsingSingleAnchorPayload) { if (payloads != null && payloads.Any() && stringChanges != null) { - if (!stringChanges.Keys.Contains(tagPosition)) - stringChanges[tagPosition] = new(); - - var changes = stringChanges[tagPosition]; + var changes = stringChanges.GetChange(tagPosition); changes.Payloads.AddRange(payloads); changes.ForceUsingSingleAnchorPayload = forceUsingSingleAnchorPayload; } @@ -279,80 +209,18 @@ namespace PlayerTags.Features /// The string to apply changes to. /// The changes to apply. /// The payload in the string that changes should be anchored to. If there is no anchor, the changes will be applied to the entire string. - protected void ApplyStringChanges(SeString seString, Dictionary stringChanges, List anchorPayloads = null, Payload anchorReplacePayload = null) + protected void ApplyStringChanges(SeString seString, StringChanges stringChanges, List anchorPayloads = null, Payload anchorReplacePayload = null) { - if (stringChanges.Count == 0) + var props = new StringChangesProps { - return; - } + Destination = seString, + AnchorPayload = anchorReplacePayload + }; - List tagPositionsOrdered = new List(); - // If there's no anchor payload, do replaces first so that befores and afters are based on the replaced data - if (anchorPayloads == null || !anchorPayloads.Any()) - { - tagPositionsOrdered.Add(TagPosition.Replace); - } + props.AnchorPayloads = anchorPayloads; + props.StringChanges = stringChanges; - tagPositionsOrdered.Add(TagPosition.Before); - tagPositionsOrdered.Add(TagPosition.After); - - // If there is an anchor payload, do replaces last so that we still know which payload needs to be removed - if (anchorPayloads != null && anchorPayloads.Any()) - { - tagPositionsOrdered.Add(TagPosition.Replace); - } - - foreach (var tagPosition in tagPositionsOrdered) - { - if (stringChanges.TryGetValue(tagPosition, out var payloads) && payloads.Payloads.Any()) - { - AddSpacesBetweenTextPayloads(stringChanges[tagPosition].Payloads, tagPosition); - if (tagPosition == TagPosition.Before) - { - Payload anchorFirst = payloads.ForceUsingSingleAnchorPayload ? anchorReplacePayload : anchorPayloads?.FirstOrDefault(); - - if (anchorFirst != null) - { - var anchorPayloadIndex = seString.Payloads.IndexOf(anchorFirst); - seString.Payloads.InsertRange(anchorPayloadIndex, payloads.Payloads); - } - else - { - seString.Payloads.InsertRange(0, payloads.Payloads); - } - } - else if (tagPosition == TagPosition.After) - { - Payload anchorLast = payloads.ForceUsingSingleAnchorPayload? anchorReplacePayload : anchorPayloads?.LastOrDefault(); - - if (anchorLast != null) - { - var anchorPayloadIndex = seString.Payloads.IndexOf(anchorLast); - seString.Payloads.InsertRange(anchorPayloadIndex + 1, payloads.Payloads); - } - else - { - seString.Payloads.AddRange(payloads.Payloads); - } - } - else if (tagPosition == TagPosition.Replace) - { - Payload anchorReplace = anchorReplacePayload; - - if (anchorReplace != null) - { - var anchorPayloadIndex = seString.Payloads.IndexOf(anchorReplace); - seString.Payloads.InsertRange(anchorPayloadIndex, payloads.Payloads); - seString.Remove(anchorReplace); - } - else - { - seString.Payloads.Clear(); - seString.Payloads.AddRange(payloads.Payloads); - } - } - } - } + StringUpdateFactory.ApplyStringChanges(props); } protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, List preferedPayloads)