Added experimental LinkSelfInChat feature

This commit is contained in:
r00telement
2021-12-22 16:04:01 +00:00
parent b4f66decd5
commit f6fcd57270
7 changed files with 231 additions and 41 deletions

View File

@@ -27,6 +27,7 @@ namespace PlayerTags.Configuration
public bool IsPlayersTabAllianceVisible = true;
public bool IsPlayersTabEnemiesVisible = true;
public bool IsPlayersTabOthersVisible = false;
public bool IsLinkSelfInChatEnabled = false;
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<string, InheritableData> AllTagsChanges = new Dictionary<string, InheritableData>();

View File

@@ -68,8 +68,9 @@ namespace PlayerTags.Configuration
ImGui.Spacing();
ImGui.Spacing();
DrawHeading(Strings.Loc_Static_Development);
DrawHeading(Strings.Loc_Static_Experimental);
DrawCheckbox(nameof(m_PluginConfiguration.IsPlayerNameRandomlyGenerated), true, ref m_PluginConfiguration.IsPlayerNameRandomlyGenerated, () => m_PluginConfiguration.Save(m_PluginData));
DrawCheckbox(nameof(m_PluginConfiguration.IsLinkSelfInChatEnabled), true, ref m_PluginConfiguration.IsLinkSelfInChatEnabled, () => m_PluginConfiguration.Save(m_PluginData));
ImGui.EndTabItem();
}
@@ -279,13 +280,13 @@ namespace PlayerTags.Configuration
string itemName = tag.Name.Value;
if (m_PluginData.CustomTags.Contains(tag))
{
if (tag.Text.InheritedValue != null)
if (!string.IsNullOrWhiteSpace(tag.Text.InheritedValue))
{
itemName = tag.Text.InheritedValue;
}
else
{
itemName = "";
itemName = Strings.Loc_Static_NoText;
}
}
@@ -566,7 +567,6 @@ namespace PlayerTags.Configuration
ImGui.SetTooltip(Strings.Loc_Static_AddPropertyOverride_Description);
}
// Render all the property overrides, and optionally allow the inherited properties to be rendered
IEnumerable<KeyValuePair<string, IInheritable>> inheritables = tag.Inheritables;
if (!m_PluginConfiguration.IsShowInheritedPropertiesEnabled)

View File

@@ -26,7 +26,7 @@ namespace PlayerTags.Features
/// <summary>
/// The matching text payload.
/// </summary>
public TextPayload TextPayload { get; init; }
public TextPayload? TextPayload { get; init; }
/// <summary>
/// The matching game object if one exists
@@ -38,10 +38,22 @@ namespace PlayerTags.Features
/// </summary>
public PlayerPayload? PlayerPayload { get; init; }
public StringMatch(SeString seString, TextPayload textPayload)
public Payload? PreferredPayload
{
get
{
if (TextPayload != null)
{
return TextPayload;
}
return PlayerPayload;
}
}
public StringMatch(SeString seString)
{
SeString = seString;
TextPayload = textPayload;
}
/// <summary>
@@ -55,7 +67,17 @@ namespace PlayerTags.Features
return GameObject.Name.TextValue;
}
return TextPayload.Text;
if (TextPayload != null)
{
return TextPayload.Text;
}
if (PlayerPayload != null)
{
return PlayerPayload.PlayerName;
}
return SeString.TextValue;
}
}
@@ -118,23 +140,24 @@ namespace PlayerTags.Features
{
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 && seString.Payloads[payloadIndex + 1] is TextPayload textPayload)
if (payloadIndex + 1 < seString.Payloads.Count)
{
var stringMatch = new StringMatch(seString, textPayload)
{
GameObject = gameObject,
PlayerPayload = playerPayload
};
stringMatches.Add(stringMatch);
textPayload = seString.Payloads[payloadIndex + 1] as TextPayload;
// Don't handle the text payload twice
payloadIndex++;
}
else
var stringMatch = new StringMatch(seString)
{
PluginLog.Error("Expected payload after player payload to be a text payload but it wasn't");
}
GameObject = gameObject,
PlayerPayload = playerPayload,
TextPayload = textPayload
};
stringMatches.Add(stringMatch);
}
}
@@ -202,26 +225,7 @@ namespace PlayerTags.Features
}
// An additional step to apply text color to additional locations
if (stringMatch.GameObject is PlayerCharacter playerCharacter1)
{
if (m_PluginData.JobTags.TryGetValue(playerCharacter1.ClassJob.GameData.Abbreviation, out var jobTag))
{
if (IsTagVisible(jobTag, stringMatch.GameObject))
{
if (jobTag.TextColor.InheritedValue != null)
{
if (jobTag.IsTextColorAppliedToChatName.InheritedValue != null && jobTag.IsTextColorAppliedToChatName.InheritedValue.Value)
{
int payloadIndex = message.Payloads.IndexOf(stringMatch.TextPayload);
message.Payloads.Insert(payloadIndex + 1, new UIForegroundPayload(0));
message.Payloads.Insert(payloadIndex, (new UIForegroundPayload(jobTag.TextColor.InheritedValue.Value)));
}
}
}
}
}
if (stringMatch.PlayerPayload != null)
if (stringMatch.PlayerPayload != null && stringMatch.PreferredPayload != null)
{
foreach (var customTag in m_PluginData.CustomTags)
{
@@ -233,7 +237,7 @@ namespace PlayerTags.Features
{
if (customTag.IsTextColorAppliedToChatName.InheritedValue != null && customTag.IsTextColorAppliedToChatName.InheritedValue.Value)
{
int payloadIndex = message.Payloads.IndexOf(stringMatch.TextPayload);
int payloadIndex = message.Payloads.IndexOf(stringMatch.PreferredPayload);
message.Payloads.Insert(payloadIndex + 1, new UIForegroundPayload(0));
message.Payloads.Insert(payloadIndex, (new UIForegroundPayload(customTag.TextColor.InheritedValue.Value)));
}
@@ -241,9 +245,28 @@ namespace PlayerTags.Features
}
}
}
if (stringMatch.GameObject is PlayerCharacter playerCharacter1)
{
if (m_PluginData.JobTags.TryGetValue(playerCharacter1.ClassJob.GameData.Abbreviation, out var jobTag))
{
if (IsTagVisible(jobTag, stringMatch.GameObject))
{
if (jobTag.TextColor.InheritedValue != null)
{
if (jobTag.IsTextColorAppliedToChatName.InheritedValue != null && jobTag.IsTextColorAppliedToChatName.InheritedValue.Value)
{
int payloadIndex = message.Payloads.IndexOf(stringMatch.PreferredPayload);
message.Payloads.Insert(payloadIndex + 1, new UIForegroundPayload(0));
message.Payloads.Insert(payloadIndex, (new UIForegroundPayload(jobTag.TextColor.InheritedValue.Value)));
}
}
}
}
}
}
ApplyStringChanges(message, stringChanges, stringMatch.TextPayload);
ApplyStringChanges(message, stringChanges, stringMatch.PreferredPayload);
}
}
}

View File

@@ -0,0 +1,115 @@
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Logging;
using PlayerTags.Configuration;
using PlayerTags.Data;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PlayerTags.Features
{
public class LinkSelfInChatFeature : IDisposable
{
private PluginConfiguration m_PluginConfiguration;
private PluginData m_PluginData;
public LinkSelfInChatFeature(PluginConfiguration pluginConfiguration, PluginData pluginData)
{
m_PluginConfiguration = pluginConfiguration;
m_PluginData = pluginData;
PluginServices.ChatGui.ChatMessage += Chat_ChatMessage;
}
public void Dispose()
{
PluginServices.ChatGui.ChatMessage -= Chat_ChatMessage;
}
private void Chat_ChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled)
{
if (m_PluginConfiguration.IsLinkSelfInChatEnabled)
{
ParsePayloads(sender);
ParsePayloads(message);
}
}
private void ParsePayloads(SeString seString)
{
if (PluginServices.ClientState.LocalPlayer != null)
{
foreach (var payload in seString.Payloads.ToArray())
{
if (payload is not TextPayload textPayload)
{
continue;
}
List<TextPayload> playerTextPayloads = new List<TextPayload>();
var playerName = PluginServices.ClientState.LocalPlayer.Name.TextValue;
if (textPayload.Text == playerName)
{
playerTextPayloads.Add(textPayload);
textPayload.Text = textPayload.Text;
}
else
{
var textMatchIndex = textPayload.Text.IndexOf(playerName);
while (textMatchIndex >= 0)
{
var textPayloadIndex = seString.Payloads.IndexOf(payload);
// Chop text to the left and insert it as a new payload
if (textMatchIndex > 0)
{
seString.Payloads.Insert(textPayloadIndex, new TextPayload(textPayload.Text.Substring(0, textMatchIndex)));
// Remove from the chopped text from the original payload
textPayload.Text = textPayload.Text.Substring(textMatchIndex, textPayload.Text.Length - textMatchIndex);
}
// This is the last reference to the local player in this payload
if (textPayload.Text.Length == playerName.Length)
{
playerTextPayloads.Add(textPayload);
break;
}
// Create the new name payload and add it
var playerTextPayload = new TextPayload(playerName);
playerTextPayloads.Add(playerTextPayload);
seString.Payloads.Insert(textPayloadIndex, playerTextPayload);
// Remove from the chopped text from the original payload
textPayload.Text = textPayload.Text.Substring(0, playerName.Length);
textMatchIndex = textPayload.Text.IndexOf(playerName);
}
}
foreach (var playerTextPayload in playerTextPayloads)
{
// This does some dodgy shit for an unknown reason.
// Typically when you receive a player payload followed by a text payload, it displays the text
// and links it with the player payload. When trying to make one of these manually, it displays the player payload separately,
// effectively doubling up the player name.
//var playerPayload = new PlayerPayload(playerName, PluginServices.ClientState.LocalPlayer.HomeWorld.Id);
//seString.Payloads.Insert(seString.Payloads.IndexOf(playerTextPayload), playerPayload);
// 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);
seString.Payloads.Insert(seString.Payloads.IndexOf(playerTextPayload), playerPayload);
seString.Payloads.Remove(playerTextPayload);
}
}
}
}
}
}

View File

@@ -18,6 +18,7 @@ namespace PlayerTags
private PluginData m_PluginData;
private PluginConfigurationUI m_PluginConfigurationUI;
private LinkSelfInChatFeature m_LinkSelfInChatFeature;
private CustomTagsContextMenuFeature m_CustomTagsContextMenuFeature;
private NameplatesTagTargetFeature m_NameplatesTagTargetFeature;
private ChatTagTargetFeature m_ChatTagTargetFeature;
@@ -38,6 +39,7 @@ namespace PlayerTags
m_PluginConfiguration.IsVisible = true;
m_PluginConfiguration.Save(m_PluginData);
}) { HelpMessage = "Shows the config" });
m_LinkSelfInChatFeature = new LinkSelfInChatFeature(m_PluginConfiguration, m_PluginData);
m_CustomTagsContextMenuFeature = new CustomTagsContextMenuFeature(m_XivCommon, m_PluginConfiguration, m_PluginData);
m_NameplatesTagTargetFeature = new NameplatesTagTargetFeature(m_PluginConfiguration, m_PluginData);
m_ChatTagTargetFeature = new ChatTagTargetFeature(m_PluginConfiguration, m_PluginData);
@@ -47,7 +49,8 @@ namespace PlayerTags
{
m_ChatTagTargetFeature.Dispose();
m_NameplatesTagTargetFeature.Dispose();
m_CustomTagsContextMenuFeature.Dispose();
m_CustomTagsContextMenuFeature.Dispose();
m_LinkSelfInChatFeature.Dispose();
PluginServices.CommandManager.RemoveHandler(c_CommandName);
PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi -= UiBuilder_OpenConfigUi;
PluginServices.DalamudPluginInterface.UiBuilder.Draw -= UiBuilder_Draw;

View File

@@ -240,6 +240,24 @@ namespace PlayerTags.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Detect references to self in chat.
/// </summary>
public static string Loc_IsLinkSelfInChatEnabled {
get {
return ResourceManager.GetString("Loc_IsLinkSelfInChatEnabled", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Attempts to detect references to self in chat. This allows tags to appear in chat for yourself..
/// </summary>
public static string Loc_IsLinkSelfInChatEnabled_Description {
get {
return ResourceManager.GetString("Loc_IsLinkSelfInChatEnabled_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Randomly generate player names.
/// </summary>
@@ -1041,6 +1059,15 @@ namespace PlayerTags.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Experimental.
/// </summary>
public static string Loc_Static_Experimental {
get {
return ResourceManager.GetString("Loc_Static_Experimental", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Add &apos;{0}&apos; to {1}..
/// </summary>
@@ -1086,6 +1113,15 @@ namespace PlayerTags.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to &lt;No text&gt;.
/// </summary>
public static string Loc_Static_NoText {
get {
return ResourceManager.GetString("Loc_Static_NoText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Player.
/// </summary>

View File

@@ -252,15 +252,27 @@
<data name="Loc_Static_Development" xml:space="preserve">
<value>Development</value>
</data>
<data name="Loc_Static_Experimental" xml:space="preserve">
<value>Experimental</value>
</data>
<data name="Loc_IsPlayerNameRandomlyGenerated" xml:space="preserve">
<value>Randomly generate player names</value>
</data>
<data name="Loc_IsPlayerNameRandomlyGenerated_Description" xml:space="preserve">
<value>Replace every player's name with a randomly generated one.</value>
</data>
<data name="Loc_IsLinkSelfInChatEnabled" xml:space="preserve">
<value>Detect references to self in chat</value>
</data>
<data name="Loc_IsLinkSelfInChatEnabled_Description" xml:space="preserve">
<value>Attempts to detect references to self in chat. This allows tags to appear in chat for yourself.</value>
</data>
<data name="Loc_Static_Tags" xml:space="preserve">
<value>Tags</value>
</data>
<data name="Loc_Static_NoText" xml:space="preserve">
<value>&lt;No text&gt;</value>
</data>
<data name="Loc_AllTags" xml:space="preserve">
<value>All</value>
</data>