diff --git a/PlayerTags/Features/ChatTagTargetFeature.cs b/PlayerTags/Features/ChatTagTargetFeature.cs
index 72508c7..aab575e 100644
--- a/PlayerTags/Features/ChatTagTargetFeature.cs
+++ b/PlayerTags/Features/ChatTagTargetFeature.cs
@@ -28,10 +28,7 @@ namespace PlayerTags.Features
///
public SeString SeString { get; init; }
- ///
- /// The matching text payload.
- ///
- public TextPayload? TextPayload { get; init; }
+ public List DisplayTextPayloads { get; init; } = new();
///
/// The matching game object if one exists
@@ -43,16 +40,18 @@ namespace PlayerTags.Features
///
public PlayerPayload? PlayerPayload { get; init; }
- public Payload? PreferredPayload
+ public Payload? PlayerNamePayload
{
get
{
- if (TextPayload != null)
- {
- return TextPayload;
- }
+ Payload textPayload = null;
+ string textMatch = GetMatchText();
- return PlayerPayload;
+ textPayload = DisplayTextPayloads.FirstOrDefault(n => n is TextPayload textPayload && textPayload.Text.Contains(textMatch));
+ textPayload ??= PlayerPayload;
+ textPayload ??= DisplayTextPayloads.FirstOrDefault();
+
+ return textPayload;
}
}
@@ -72,11 +71,6 @@ namespace PlayerTags.Features
return GameObject.Name.TextValue;
}
- if (TextPayload != null)
- {
- return TextPayload.Text;
- }
-
if (PlayerPayload != null)
{
return PlayerPayload.PlayerName;
@@ -107,8 +101,8 @@ namespace PlayerTags.Features
{
if (m_PluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext].IsApplyTagsToAllChatMessagesEnabled || Enum.IsDefined(type))
{
- AddTagsToChat(sender);
- AddTagsToChat(message);
+ AddTagsToChat(sender, type, true);
+ AddTagsToChat(message, type, false);
}
}
@@ -139,31 +133,44 @@ namespace PlayerTags.Features
/// A list of matched game objects.
private List GetStringMatches(SeString seString)
{
- List stringMatches = new List();
+ List stringMatches = new();
+ Stack curPlayerPayload = new();
+ Stack> curRefPayloads = new();
+ var defaultRawPayload = RawPayload.LinkTerminator.Data;
- for (int payloadIndex = 0; payloadIndex < seString.Payloads.Count; ++payloadIndex)
+ foreach (var payload in seString.Payloads)
{
- var payload = seString.Payloads[payloadIndex];
+
if (payload is PlayerPayload playerPayload)
+ {
+ curPlayerPayload.Push(playerPayload);
+ curRefPayloads.Push(new List());
+ }
+ else if (payload is RawPayload rawPayload)
+ {
+ if (defaultRawPayload.SequenceEqual(rawPayload.Data))
+ finishCurrentMatch();
+ }
+ else
+ {
+ if (curRefPayloads.TryPeek(out List result))
+ result.Add(payload);
+ }
+ }
+
+ // Finally finish, if not closed by RawPayload
+ finishCurrentMatch();
+
+ void finishCurrentMatch()
+ {
+ if (curPlayerPayload.TryPop(out PlayerPayload playerPayload))
{
var gameObject = PluginServices.ObjectTable.FirstOrDefault(gameObject => gameObject.Name.TextValue == playerPayload.PlayerName);
-
- TextPayload? textPayload = null;
-
- // The next payload MUST be a text payload
- if (payloadIndex + 1 < seString.Payloads.Count)
- {
- textPayload = seString.Payloads[payloadIndex + 1] as TextPayload;
-
- // Don't handle the text payload twice
- payloadIndex++;
- }
-
var stringMatch = new StringMatch(seString)
{
GameObject = gameObject,
PlayerPayload = playerPayload,
- TextPayload = textPayload
+ DisplayTextPayloads = curRefPayloads.Pop()
};
stringMatches.Add(stringMatch);
}
@@ -172,12 +179,45 @@ namespace PlayerTags.Features
return stringMatches;
}
+ private void SplitOffPartyNumberPrefix(SeString sender, XivChatType type)
+ {
+ if (type == XivChatType.Party || type == XivChatType.Alliance)
+ {
+ PlayerPayload lastPlayerPayload = null;
+ foreach (var payload in sender.Payloads.ToArray())
+ {
+ if (payload is PlayerPayload playerPayload)
+ lastPlayerPayload = playerPayload;
+ else if (payload is TextPayload playerNamePayload && lastPlayerPayload != null)
+ {
+ // Get position of player name in payload
+ var indexOfPlayerName = playerNamePayload.Text.IndexOf(lastPlayerPayload.PlayerName);
+
+ if (indexOfPlayerName > 0)
+ {
+ // Split off the name from the prefix number
+ var prefixPayload = new TextPayload(playerNamePayload.Text[..indexOfPlayerName]);
+ playerNamePayload.Text = playerNamePayload.Text[indexOfPlayerName..];
+
+ // Add prefix number before the player name payload
+ var playerNamePayloadIndex = sender.Payloads.IndexOf(playerNamePayload);
+ sender.Payloads.Insert(playerNamePayloadIndex, prefixPayload);
+ }
+ }
+ }
+ }
+ }
+
///
/// Adds all configured tags to a chat message.
///
/// The message to change.
- private void AddTagsToChat(SeString message)
+ private void AddTagsToChat(SeString message, XivChatType chatType, bool isSender)
{
+ // Split out the party/alliance number from the PlayerPayload
+ if (isSender)
+ SplitOffPartyNumberPrefix(message, chatType);
+
var stringMatches = GetStringMatches(message);
foreach (var stringMatch in stringMatches)
{
@@ -235,7 +275,7 @@ namespace PlayerTags.Features
}
// An additional step to apply text color to additional locations
- if (stringMatch.PlayerPayload != null && stringMatch.PreferredPayload != null)
+ if (stringMatch.PlayerPayload != null && stringMatch.DisplayTextPayloads.Any())
{
Identity identity = m_PluginData.GetIdentity(stringMatch.PlayerPayload);
foreach (var customTagId in identity.CustomTagIds)
@@ -252,10 +292,10 @@ namespace PlayerTags.Features
}
void applyTextFormatting(Tag tag)
- => ApplyTextFormatting(stringMatch.GameObject, tag, new[] { message }, new[] { tag.IsTextColorAppliedToChatName }, stringMatch.PreferredPayload);
+ => ApplyTextFormatting(stringMatch.GameObject, tag, new[] { message }, new[] { tag.IsTextColorAppliedToChatName }, stringMatch.DisplayTextPayloads);
}
- ApplyStringChanges(message, stringChanges, stringMatch.PreferredPayload);
+ ApplyStringChanges(message, stringChanges, stringMatch.DisplayTextPayloads, stringMatch.PlayerNamePayload);
}
// Replace PlayerPayloads of your own character with TextPayloads
diff --git a/PlayerTags/Features/LinkSelfInChatFeature.cs b/PlayerTags/Features/LinkSelfInChatFeature.cs
index d1a078e..7f346cc 100644
--- a/PlayerTags/Features/LinkSelfInChatFeature.cs
+++ b/PlayerTags/Features/LinkSelfInChatFeature.cs
@@ -33,12 +33,12 @@ namespace PlayerTags.Features
{
if (m_PluginConfiguration.GeneralOptions[activityContextManager.CurrentActivityContext].IsLinkSelfInChatEnabled)
{
- ParsePayloads(sender);
- ParsePayloads(message);
+ ParsePayloads(sender, type, true);
+ ParsePayloads(message, type, false);
}
}
- private void ParsePayloads(SeString seString)
+ private void ParsePayloads(SeString seString, XivChatType chatType, bool isSender)
{
if (PluginServices.ClientState.LocalPlayer != null)
{
@@ -101,7 +101,10 @@ namespace PlayerTags.Features
// For now, don't follow up with a text payload. Only use a player payload.
var playerPayload = new PlayerPayload(playerName, PluginServices.ClientState.LocalPlayer.HomeWorld.Id);
- var playerPayloadIndex = seString.Payloads.IndexOf(playerTextPayload);
+ int playerPayloadIndex = seString.Payloads.IndexOf(playerTextPayload);
+
+ if (isSender && (chatType == XivChatType.Party || chatType == XivChatType.Alliance))
+ playerPayloadIndex--;
// Add the Player Link Payload
seString.Payloads.Insert(playerPayloadIndex++, playerPayload);
diff --git a/PlayerTags/Features/TagTargetFeature.cs b/PlayerTags/Features/TagTargetFeature.cs
index 8261f53..911c697 100644
--- a/PlayerTags/Features/TagTargetFeature.cs
+++ b/PlayerTags/Features/TagTargetFeature.cs
@@ -240,7 +240,7 @@ 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, Payload? anchorPayload = null)
+ protected void ApplyStringChanges(SeString seString, Dictionary> stringChanges, List anchorPayloads = null, Payload anchorReplacePayload = null)
{
if (stringChanges.Count == 0)
{
@@ -249,7 +249,7 @@ namespace PlayerTags.Features
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 (anchorPayload == null)
+ if (anchorPayloads == null || !anchorPayloads.Any())
{
tagPositionsOrdered.Add(TagPosition.Replace);
}
@@ -258,7 +258,7 @@ namespace PlayerTags.Features
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 (anchorPayload != null)
+ if (anchorPayloads != null && anchorPayloads.Any())
{
tagPositionsOrdered.Add(TagPosition.Replace);
}
@@ -270,8 +270,9 @@ namespace PlayerTags.Features
AddSpacesBetweenTextPayloads(stringChanges[tagPosition], tagPosition);
if (tagPosition == TagPosition.Before)
{
- if (anchorPayload != null)
+ if (anchorPayloads != null && anchorPayloads.Any())
{
+ var anchorPayload = anchorPayloads.First();
var anchorPayloadIndex = seString.Payloads.IndexOf(anchorPayload);
seString.Payloads.InsertRange(anchorPayloadIndex, payloads);
}
@@ -282,8 +283,9 @@ namespace PlayerTags.Features
}
else if (tagPosition == TagPosition.After)
{
- if (anchorPayload != null)
+ if (anchorPayloads != null && anchorPayloads.Any())
{
+ var anchorPayload = anchorPayloads.Last();
var anchorPayloadIndex = seString.Payloads.IndexOf(anchorPayload);
seString.Payloads.InsertRange(anchorPayloadIndex + 1, payloads);
}
@@ -294,11 +296,11 @@ namespace PlayerTags.Features
}
else if (tagPosition == TagPosition.Replace)
{
- if (anchorPayload != null)
+ if (anchorReplacePayload != null)
{
- var anchorPayloadIndex = seString.Payloads.IndexOf(anchorPayload);
+ var anchorPayloadIndex = seString.Payloads.IndexOf(anchorReplacePayload);
seString.Payloads.InsertRange(anchorPayloadIndex, payloads);
- seString.Payloads.Remove(anchorPayload);
+ seString.Payloads.Remove(anchorReplacePayload);
}
else
{
@@ -310,7 +312,7 @@ namespace PlayerTags.Features
}
}
- protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, Payload preferedPayload)
+ protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue[] textColorApplied, List preferedPayloads)
{
if (IsTagVisible(tag, gameObject))
{
@@ -355,10 +357,10 @@ namespace PlayerTags.Features
void applyTextFormattingPayloads(SeString destPayload, Payload startPayload, Payload endPayload)
{
- if (preferedPayload == null)
+ if (preferedPayloads == null || !preferedPayloads.Any())
applyTextFormattingPayloadToStartAndEnd(destPayload, startPayload, endPayload);
else
- applyTextFormattingPayloadsToSpecificPosition(destPayload, startPayload, endPayload, preferedPayload);
+ applyTextFormattingPayloadsToSpecificPosition(destPayload, startPayload, endPayload, preferedPayloads);
}
void applyTextFormattingPayloadToStartAndEnd(SeString destPayload, Payload startPayload, Payload endPayload)
@@ -367,11 +369,13 @@ namespace PlayerTags.Features
destPayload.Payloads.Add(endPayload);
}
- void applyTextFormattingPayloadsToSpecificPosition(SeString destPayload, Payload startPayload, Payload endPayload, Payload preferedPayload)
+ void applyTextFormattingPayloadsToSpecificPosition(SeString destPayload, Payload startPayload, Payload endPayload, List preferedPayload)
{
- int payloadIndex = destPayload.Payloads.IndexOf(preferedPayload);
- destPayload.Payloads.Insert(payloadIndex + 1, endPayload);
- destPayload.Payloads.Insert(payloadIndex, startPayload);
+ int payloadStartIndex = destPayload.Payloads.IndexOf(preferedPayloads.First());
+ destPayload.Payloads.Insert(payloadStartIndex, startPayload);
+
+ int payloadEndIndex = destPayload.Payloads.IndexOf(preferedPayloads.Last());
+ destPayload.Payloads.Insert(payloadEndIndex + 1, endPayload);
}
}
}